From b8e10f320d4a9cbe24433db68f8fd43d42c25715 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Sat, 30 May 2015 03:37:43 +0900 Subject: [PATCH] update snippet fields --- build.config.js | 5 - src/directives/tags.js | 13 + src/directives/ui-ace.js | 337 ++++++++++++++++++++++++++ src/index.html | 1 - src/modals/new-snippet-modal.js | 3 +- src/modals/new-snippet-modal.tpl.html | 20 +- src/states/home/home.tpl.html | 3 + src/states/snippets/detail.tpl.html | 14 +- src/states/snippets/list.tpl.html | 7 +- src/states/snippets/snippets.scss | 5 +- 10 files changed, 376 insertions(+), 32 deletions(-) create mode 100644 src/directives/tags.js create mode 100644 src/directives/ui-ace.js diff --git a/build.config.js b/build.config.js index 34aef705..7357c923 100644 --- a/build.config.js +++ b/build.config.js @@ -10,11 +10,6 @@ module.exports = { src:'node_modules/angular/angular.js', cdn:'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js' }, - { - name:'angular-ui-ace', - src:'node_modules/@rokt33r/angular-ui-ace/src/ui-ace.js', - cdn:'https://cdn.rawgit.com/angular-ui/ui-ace/v0.2.3/ui-ace.min.js' - }, { name:'angular-bootstrap', src:'node_modules/angular-bootstrap/dist/ui-bootstrap-tpls.js', diff --git a/src/directives/tags.js b/src/directives/tags.js new file mode 100644 index 00000000..ce2411e5 --- /dev/null +++ b/src/directives/tags.js @@ -0,0 +1,13 @@ +angular.module('codexen.directives') + .directive('tags', function () { + return { + restrict:'A', + template: '

'+ + ' '+ + '#'+ + '

', + scope:{ + tags: '=' + } + } + }) diff --git a/src/directives/ui-ace.js b/src/directives/ui-ace.js new file mode 100644 index 00000000..f50ee49c --- /dev/null +++ b/src/directives/ui-ace.js @@ -0,0 +1,337 @@ +'use strict'; + +/** + * Binds a ACE Editor widget + */ +angular.module('ui.ace', []) + .constant('uiAceConfig', {}) + .directive('uiAce', ['uiAceConfig', function (uiAceConfig) { + + if (angular.isUndefined(window.ace)) { + throw new Error('ui-ace need ace to work... (o rly?)'); + } + + /** + * Sets editor options such as the wrapping mode or the syntax checker. + * + * The supported options are: + * + * + * + * @param acee + * @param session ACE editor session + * @param {object} opts Options to be set + */ + var setOptions = function(acee, session, opts) { + + // sets the ace worker path, if running from concatenated + // or minified source + if (angular.isDefined(opts.workerPath)) { + var config = window.ace.require('ace/config'); + config.set('workerPath', opts.workerPath); + } + // ace requires loading + if (angular.isDefined(opts.require)) { + opts.require.forEach(function (n) { + window.ace.require(n); + }); + } + // Boolean options + if (angular.isDefined(opts.showGutter)) { + acee.renderer.setShowGutter(opts.showGutter); + } + if (angular.isDefined(opts.useWrapMode)) { + session.setUseWrapMode(opts.useWrapMode); + } + if (angular.isDefined(opts.showInvisibles)) { + acee.renderer.setShowInvisibles(opts.showInvisibles); + } + if (angular.isDefined(opts.showIndentGuides)) { + acee.renderer.setDisplayIndentGuides(opts.showIndentGuides); + } + if (angular.isDefined(opts.useSoftTabs)) { + session.setUseSoftTabs(opts.useSoftTabs); + } + if (angular.isDefined(opts.showPrintMargin)) { + acee.setShowPrintMargin(opts.showPrintMargin); + } + if (angular.isDefined(opts.maxLines)) { + if(opts.maxLines < 0) opts.maxLines = Infinity + acee.setOptions({ + maxLines: opts.maxLines + }) + } + + // commands + if (angular.isDefined(opts.disableSearch) && opts.disableSearch) { + acee.commands.addCommands([ + { + name: 'unfind', + bindKey: { + win: 'Ctrl-F', + mac: 'Command-F' + }, + exec: function () { + return false; + }, + readOnly: true + } + ]); + } + + // Basic options + if (angular.isString(opts.theme)) { + acee.setTheme('ace/theme/' + opts.theme); + } + if (angular.isString(opts.mode)) { + session.setMode('ace/mode/' + opts.mode); + } + // Advanced options + if (angular.isDefined(opts.firstLineNumber)) { + if (angular.isNumber(opts.firstLineNumber)) { + session.setOption('firstLineNumber', opts.firstLineNumber); + } else if (angular.isFunction(opts.firstLineNumber)) { + session.setOption('firstLineNumber', opts.firstLineNumber()); + } + } + + // advanced options + var key, obj; + if (angular.isDefined(opts.advanced)) { + for (key in opts.advanced) { + // create a javascript object with the key and value + obj = { name: key, value: opts.advanced[key] }; + // try to assign the option to the ace editor + acee.setOption(obj.name, obj.value); + } + } + + // advanced options for the renderer + if (angular.isDefined(opts.rendererOptions)) { + for (key in opts.rendererOptions) { + // create a javascript object with the key and value + obj = { name: key, value: opts.rendererOptions[key] }; + // try to assign the option to the ace editor + acee.renderer.setOption(obj.name, obj.value); + } + } + + // onLoad callbacks + angular.forEach(opts.callbacks, function (cb) { + if (angular.isFunction(cb)) { + cb(acee); + } + }); + }; + + return { + restrict: 'EA', + require: '?ngModel', + link: function (scope, elm, attrs, ngModel) { + + /** + * Corresponds the uiAceConfig ACE configuration. + * @type object + */ + var options = uiAceConfig.ace || {}; + + /** + * uiAceConfig merged with user options via json in attribute or data binding + * @type object + */ + var opts = angular.extend({}, options, scope.$eval(attrs.uiAce)); + + /** + * ACE editor + * @type object + */ + var acee = window.ace.edit(elm[0]); + acee.$blockScrolling = Infinity + + /** + * ACE editor session. + * @type object + * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session} + */ + var session = acee.getSession(); + + /** + * Reference to a change listener created by the listener factory. + * @function + * @see listenerFactory.onChange + */ + var onChangeListener; + + /** + * Reference to a blur listener created by the listener factory. + * @function + * @see listenerFactory.onBlur + */ + var onBlurListener; + + /** + * Calls a callback by checking its existing. The argument list + * is variable and thus this function is relying on the arguments + * object. + * @throws {Error} If the callback isn't a function + */ + var executeUserCallback = function () { + + /** + * The callback function grabbed from the array-like arguments + * object. The first argument should always be the callback. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {*} + */ + var callback = arguments[0]; + + /** + * Arguments to be passed to the callback. These are taken + * from the array-like arguments object. The first argument + * is stripped because that should be the callback function. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {Array} + */ + var args = Array.prototype.slice.call(arguments, 1); + + if (angular.isDefined(callback)) { + scope.$evalAsync(function () { + if (angular.isFunction(callback)) { + callback(args); + } else { + throw new Error('ui-ace use a function as callback.'); + } + }); + } + }; + + /** + * Listener factory. Until now only change listeners can be created. + * @type object + */ + var listenerFactory = { + /** + * Creates a change listener which propagates the change event + * and the editor session to the callback from the user option + * onChange. It might be exchanged during runtime, if this + * happens the old listener will be unbound. + * + * @param callback callback function defined in the user options + * @see onChangeListener + */ + onChange: function (callback) { + return function (e) { + var newValue = session.getValue(); + + if (ngModel && newValue !== ngModel.$viewValue && + // HACK make sure to only trigger the apply outside of the + // digest loop 'cause ACE is actually using this callback + // for any text transformation ! + !scope.$$phase && !scope.$root.$$phase) { + scope.$evalAsync(function () { + ngModel.$setViewValue(newValue); + }); + } + + executeUserCallback(callback, e, acee); + }; + }, + /** + * Creates a blur listener which propagates the editor session + * to the callback from the user option onBlur. It might be + * exchanged during runtime, if this happens the old listener + * will be unbound. + * + * @param callback callback function defined in the user options + * @see onBlurListener + */ + onBlur: function (callback) { + return function () { + executeUserCallback(callback, acee); + }; + } + }; + + attrs.$observe('readonly', function (value) { + acee.setReadOnly(!!value || value === ''); + }); + + // Value Blind + if (ngModel) { + ngModel.$formatters.push(function (value) { + if (angular.isUndefined(value) || value === null) { + return ''; + } + else if (angular.isObject(value) || angular.isArray(value)) { + throw new Error('ui-ace cannot use an object or an array as a model'); + } + return value; + }); + + ngModel.$render = function () { + session.setValue(ngModel.$viewValue); + }; + } + + // Listen for option updates + var updateOptions = function (current, previous) { + if (current === previous) return; + opts = angular.extend({}, options, scope.$eval(attrs.uiAce)); + + opts.callbacks = [ opts.onLoad ]; + if (opts.onLoad !== options.onLoad) { + // also call the global onLoad handler + opts.callbacks.unshift(options.onLoad); + } + + // EVENTS + + // unbind old change listener + session.removeListener('change', onChangeListener); + + // bind new change listener + onChangeListener = listenerFactory.onChange(opts.onChange); + session.on('change', onChangeListener); + + // unbind old blur listener + //session.removeListener('blur', onBlurListener); + acee.removeListener('blur', onBlurListener); + + // bind new blur listener + onBlurListener = listenerFactory.onBlur(opts.onBlur); + acee.on('blur', onBlurListener); + + setOptions(acee, session, opts); + }; + + scope.$watch(attrs.uiAce, updateOptions, /* deep watch */ true); + + // set the options here, even if we try to watch later, if this + // line is missing things go wrong (and the tests will also fail) + updateOptions(options); + + elm.on('$destroy', function () { + acee.session.$stopWorker(); + acee.destroy(); + }); + + scope.$watch(function() { + return [elm[0].offsetWidth, elm[0].offsetHeight]; + }, function() { + acee.resize(); + acee.renderer.updateFull(); + }, true); + + scope.Infinity = -1 + + } + }; + }]); diff --git a/src/index.html b/src/index.html index a308531b..73b43295 100644 --- a/src/index.html +++ b/src/index.html @@ -26,7 +26,6 @@ - diff --git a/src/modals/new-snippet-modal.js b/src/modals/new-snippet-modal.js index 8ea5f694..28b18bb6 100644 --- a/src/modals/new-snippet-modal.js +++ b/src/modals/new-snippet-modal.js @@ -18,9 +18,8 @@ angular.module('codexen.modals') vm.submit = function () { var params = { - title: vm.title, description: vm.description, - prefix: vm.prefix, + callSign: vm.callSign, mode: vm.mode==null?null:vm.mode.name.toLowerCase(), content: vm.content, tags: angular.isArray(vm.tags)?vm.tags.map(function (tag) { return {_id: tag._id, name: tag.name} }):[] diff --git a/src/modals/new-snippet-modal.tpl.html b/src/modals/new-snippet-modal.tpl.html index 84e838ac..aa810d8b 100644 --- a/src/modals/new-snippet-modal.tpl.html +++ b/src/modals/new-snippet-modal.tpl.html @@ -1,6 +1,6 @@
- + {{$select.selected.name}} @@ -18,13 +18,15 @@
-
+
+ }" + ng-model="vm.content" + >
+
@@ -34,8 +36,6 @@
(new)
- -
diff --git a/src/states/home/home.tpl.html b/src/states/home/home.tpl.html index 007d21cb..651752f8 100644 --- a/src/states/home/home.tpl.html +++ b/src/states/home/home.tpl.html @@ -5,6 +5,9 @@ App: Rokt33r/codexen-app
Server: Rokt33r/codexen-server +

+

+ © 2015 MAISIN&CO.,Inc.

diff --git a/src/states/snippets/detail.tpl.html b/src/states/snippets/detail.tpl.html index deecb1e9..0748b984 100644 --- a/src/states/snippets/detail.tpl.html +++ b/src/states/snippets/detail.tpl.html @@ -1,9 +1,7 @@
- - Title : - + call sign : @@ -18,15 +16,15 @@
-

- -

-

+
-

+

created at

-

-

- -

+
diff --git a/src/states/snippets/snippets.scss b/src/states/snippets/snippets.scss index 867922fd..62754ad3 100644 --- a/src/states/snippets/snippets.scss +++ b/src/states/snippets/snippets.scss @@ -47,6 +47,8 @@ $snippet-list-item-hover-bg: #EEE; bottom: 0; left: $left-pane-width; right: 0; + overflow-x: hidden; + overflow-y: auto; } } @@ -69,13 +71,14 @@ $snippet-list-item-hover-bg: #EEE; .detail-body{ padding: 5px 10px; .ace_editor { - height: 500px; + min-height: 300px; border: solid 1px $border-color; border-radius: 5px; } } } .tags{ + word-break: break-all; a{ margin: 0 2px; }