Skip to content

Commit

Permalink
More integration work for Carbon editor enabling embedding and more e…
Browse files Browse the repository at this point in the history
…xtensions.
  • Loading branch information
mkhatib committed Dec 13, 2015
1 parent 8d4be0c commit 9f63031
Show file tree
Hide file tree
Showing 24 changed files with 230 additions and 135 deletions.
1 change: 1 addition & 0 deletions backend/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ group :production do
end

group :test, :development do
gem 'thin'
gem 'spring'
gem 'rspec-rails', '~> 3.1'
# gem 'guard-rspec'
Expand Down
10 changes: 10 additions & 0 deletions backend/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ GEM
connection_pool (2.1.1)
css_parser (1.3.6)
addressable
daemons (1.2.3)
devise_token_auth (0.1.30)
devise (~> 3.3)
rails (~> 4.1)
Expand All @@ -90,6 +91,7 @@ GEM
launchy (~> 2.1)
mail (~> 2.2)
erubis (2.7.0)
eventmachine (1.0.8)
excon (0.44.1)
factory_girl (4.3.0)
activesupport (>= 3.0.0)
Expand Down Expand Up @@ -335,6 +337,10 @@ GEM
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
thin (1.6.4)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (~> 1.0)
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
Expand Down Expand Up @@ -382,3 +388,7 @@ DEPENDENCIES
sidekiq
sidetiq
spring
thin

BUNDLED WITH
1.10.6
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ backend:
environment:
RACK_ENV: development
PORT: 80
API_HOST: dockerhost:3000
WEB_CLIENT_HOST: dockerhost:9000
API_HOST: manshar.host:3000
WEB_CLIENT_HOST: manshar.host:9000
SECRET_TOKEN: a59d98c49d5a9b6ccf7e14cc35cb005e1ba9ab6656070e4c05e5df95d07afd44f98fd48552a68ac83e6dc3440691db011ef635c0c1a74102964686e7d51f3bf3
DRAGONFLY_SECRET: c2jq0a9cj039jc01mamc09qjc930ur10fjafj0a9fj09fj23qnweocn0qwf0qfjq
MANSHAR_DB_1_PORT_5432_TCP_ADDR: db
Expand All @@ -25,7 +25,7 @@ web:
environment:
NODE_ENV: development
PORT: 9000
API_HOST: dockerhost:3000
API_HOST: manshar.host:3000
volumes:
- ./web-client/:/manshar-web-client
ports:
Expand Down
3 changes: 2 additions & 1 deletion web-client/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"ga": false,
"hljs": false,
"carbon": false,
"Imagezoom": false
"Imagezoom": false,
"Webcam": false
}
}
6 changes: 5 additions & 1 deletion web-client/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,12 @@ module.exports = function (grunt) {
// The actual grunt server settings
connect: {
options: {
protocol: 'https',
key: grunt.file.read('utils/dev/certs/server.key').toString(),
cert: grunt.file.read('utils/dev/certs/server.crt').toString(),
ca: grunt.file.read('utils/dev/certs/ca.crt').toString(),
port: grunt.option('port') || 9000,
hostname: '0.0.0.0',
hostname: grunt.option('host') || '0.0.0.0',
livereload: 35729,
middleware: function (connect, options) {
var optBase = (typeof options.base === 'string') ? [options.base] : options.base,
Expand Down
1 change: 1 addition & 0 deletions web-client/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<script src="bower_components/angular-cookies/angular-cookies.js"></script>
<script src="bower_components/angular-touch/angular-touch.js"></script>
<script src="bower_components/angular-i18n/angular-locale_ar.js"></script>
<script src="bower_components/webcamjs/webcam.min.js"></script>
<script src="bower_components/carbon/dist/carbon.js"></script>
<script src="bower_components/angular-truncate/src/truncate.js"></script>
<script src="bower_components/angulartics/src/angulartics.js"></script>
Expand Down
26 changes: 13 additions & 13 deletions web-client/app/scripts/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,94 +62,94 @@ angular.module('webClientApp', [
resolve: checkAccess
})

.when('/login', {
.when('/login/?', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl',
title: 'تسجيل الدخول',
isPublic: true,
resolve: checkAccess
})

.when('/signup', {
.when('/signup/?', {
templateUrl: 'views/signup.html',
controller: 'SignupCtrl',
title: 'مستخدم جديد',
isPublic: true,
resolve: checkAccess
})

.when('/articles/new', {
.when('/articles/new/?', {
templateUrl: 'views/articles/edit.html',
controller: 'NewArticleCtrl',
title: 'مقال جديد',
isPublic: false,
resolve: checkAccess
})

.when('/articles/:articleId/edit', {
.when('/articles/:articleId/edit/?', {
templateUrl: 'views/articles/edit.html',
controller: 'EditArticleCtrl',
isPublic: false,
resolve: checkAccess
})

.when('/articles/:articleId', {
.when('/articles/:articleId/?', {
templateUrl: 'views/articles/show.html',
controller: 'ArticleCtrl',
isPublic: true,
resolve: checkAccess
})

.when('/accounts/reset_password', {
.when('/accounts/reset_password/?', {
templateUrl: 'views/accounts/reset_password.html',
controller: 'ResetPasswordController',
isPublic: true,
resolve: checkAccess
})

.when('/accounts/update_password', {
.when('/accounts/update_password/?', {
templateUrl: 'views/accounts/update_password.html',
controller: 'UpdatePasswordController',
isPublic: true,
resolve: checkAccess
})

.when('/profiles/:userId', {
.when('/profiles/:userId/?', {
templateUrl: 'views/profiles/show.html',
controller: 'ProfileCtrl',
isPublic: true,
resolve: checkAccess
})

.when('/profiles/:userId/edit', {
.when('/profiles/:userId/edit/?', {
templateUrl: 'views/profiles/edit.html',
controller: 'EditProfileCtrl',
isPublic: false,
resolve: checkAccess
})

.when('/categories/:categoryId', {
.when('/categories/:categoryId/?', {
templateUrl: 'views/categories/show.html',
controller: 'CategoryCtrl',
isPublic: true,
resolve: checkAccess
})

.when('/categories/:categoryId/topics/:topicId', {
.when('/categories/:categoryId/topics/:topicId/?', {
templateUrl: 'views/topics/show.html',
controller: 'TopicCtrl',
isPublic: true,
resolve: checkAccess
})

.when('/admin', {
.when('/admin/?', {
templateUrl: 'views/admin/dashboard.html',
isPublic: false,
isAdmin: true,
resolve: checkAccess
})

.when('/admin/manage/categories', {
.when('/admin/manage/categories/?', {
templateUrl: 'views/admin/manage/categories.html',
controller: 'ManageCategoriesCtrl',
isPublic: false,
Expand Down
17 changes: 10 additions & 7 deletions web-client/app/scripts/directives/anchoredComments.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ angular.module('webClientApp')
var showAnchor = function(e, guidAttribute) {
// Some elements (like img inside figure) won't have guid. So rely on
// their parent guids.
var guidElement = getAncestorWithGuid(e.target, guidAttribute);
var guidElement = getAncestorWithGuid(e.currentTarget, guidAttribute);
var guid = guidElement.getAttribute(guidAttribute);
$rootScope.$emit('show-anchor', guid);
};
Expand All @@ -66,7 +66,7 @@ angular.module('webClientApp')
commentEl.setAttribute('anchored-comment', '');
commentEl.setAttribute('guid', guid);
commentEl.className = 'anchored-comment-box';
commentEl.style.top = (srcElement.offsetTop) + 'px';
commentEl.style.top = (srcElement.offsetTop + offsetTop) + 'px';
$compile(commentEl)(scope);
container.appendChild(commentEl);

Expand All @@ -84,15 +84,15 @@ angular.module('webClientApp')
* Recalculate the positions of the comments.
* @param {HTMLElement} container Element that contains the anchors.
*/
var repositionComments = function(container, guidAttribute) {
var repositionComments = function(container, offsetTop, guidAttribute) {
var comments = container.getElementsByClassName('anchored-comment-box');
for (var i = 0; i < comments.length; i++) {
var guid = comments[i].getAttribute('guid');
if (!guid) {
continue;
}
var srcElement = document.querySelector('['+guidAttribute+'="' + guid + '"]');
comments[i].style.top = srcElement.offsetTop + 'px';
comments[i].style.top = (srcElement.offsetTop + offsetTop) + 'px';
}
};

Expand Down Expand Up @@ -153,7 +153,8 @@ angular.module('webClientApp')
var guidContainer = document.getElementById(
scope.guidElementsContainerId);
angular.element(guidContainer).find('img').on('load', function() {
repositionComments(element[0], scope.guidAttribute);
repositionComments(
element[0], guidContainer.offsetTop, scope.guidAttribute);
});


Expand All @@ -168,10 +169,12 @@ angular.module('webClientApp')
var elements = guidContainer.querySelectorAll(scope.selector);

for (var i = 0; i < elements.length; i++) {
createNewComment(elements[i], scope, element[0], guidContainer.offsetTop, scope.guidAttribute);
createNewComment(
elements[i], scope, element[0],
guidContainer.offsetTop, scope.guidAttribute);
}

}, 200);
}, 1000);

});

Expand Down
12 changes: 10 additions & 2 deletions web-client/app/scripts/directives/articleRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ angular.module('webClientApp')
link: function (scope, element, attrs, ngModel) {
ngModel.$render = function() {
if (ngModel.$viewValue) {
if (!carbon.Loader.load('embedProviders')) {
carbon.Loader.register('embedProviders', {
embedly: new carbon.EmbedlyProvider({
apiKey: '46c6ad376b1343359d774c5d8a940db7'
}),
carbon: new carbon.CarbonEmbedProvider({})
});
}

var json = JSON.parse(ngModel.$viewValue);
var article = carbon.Article.fromJSON(json);
// TODO(mkhatib): Add getHTML() API to carbon.Article.
element.html(article.dom.innerHTML);
article.render(element[0]);
}
};
}
Expand Down
51 changes: 45 additions & 6 deletions web-client/app/scripts/directives/carbonEditor.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

angular.module('webClientApp')
.directive('grandeEditor', ['Image', function (Image) {
.directive('carbonEditor', ['Image', function (Image) {
return {
require: '?ngModel',
restrict: 'A',
Expand All @@ -13,6 +13,7 @@ angular.module('webClientApp')
ngModel.$render = function() {
if (ngModel.$viewValue) {
editor.loadJSON(JSON.parse(ngModel.$viewValue));
editor.render();
}
};

Expand Down Expand Up @@ -66,13 +67,26 @@ angular.module('webClientApp')

var editor = new carbon.Editor(element[0], {
modules: [
carbon.YouTubeComponent,
carbon.GiphyComponent,
carbon.EmbeddedComponent,
],
article: article,
rtl: scope.rtl || true
});

editor.install(carbon.EmbeddingExtension, {
embedProviders: carbon.Loader.load('embedProviders') || {
embedly: new carbon.EmbedlyProvider({
apiKey: '46c6ad376b1343359d774c5d8a940db7'
}),
carbon: new carbon.CarbonEmbedProvider({
})
},
ComponentClass: carbon.EmbeddedComponent
});
editor.install(carbon.SelfieExtension);
editor.render();

editor.addEventListener('change', function() {
scope.$evalAsync(read);
if (scope.onChange) {
Expand All @@ -82,11 +96,36 @@ angular.module('webClientApp')

editor.addEventListener('attachment-added', function(event) {
var attachment = event.detail.attachment;
uploadFile(attachment.file, function(src) {
attachment.setAttributes({
src: src
if (attachment.file) {
uploadFile(attachment.file, function(src) {
attachment.setAttributes({
src: src
});
});
});
} else if (attachment.dataUri) {
var dataUri = attachment.dataUri;
var timestamp = (new Date()).getTime();
var name = 'selfie-' + timestamp;
var imageFormat = '';
var match = dataUri.match(/^data\:image\/(\w+)/);
if (match) {
imageFormat = match[1];
} else {
throw 'Cannot locate image format in Data URI';
}
// extract raw base64 data from Data URI
var rawImageData = dataUri.replace(
/^data\:image\/\w+\;base64\,/, '');
var blob = new Blob(
[Webcam.base64DecToArr(rawImageData)],
{type: 'image/'+imageFormat, name: name});
blob.name = name;
uploadFile(blob, function(src) {
attachment.setAttributes({
src: src
});
});
}
});

read(); // initialize
Expand Down
5 changes: 0 additions & 5 deletions web-client/app/styles/enhanced.scss
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,6 @@ body {

article {
font-size: 1em;
figure {
iframe {
height: 450px;
}
}
blockquote {
font-size: 1.1em;
}
Expand Down
Loading

0 comments on commit 9f63031

Please sign in to comment.