//angular.module('ui.codemirror', []) // .constant('uiCodemirrorConfig', {}) // .directive('uiCodemirror', uiCodemirrorDirective); define(['app/yangui/yangui.module'], function(yangui) { yangui.register.constant('uiCodemirrorConfig', {}); yangui.register.directive('uiCodemirror', uiCodemirrorDirective); /** * @ngInject */ function uiCodemirrorDirective($timeout, uiCodemirrorConfig) { return { restrict: 'EA', require: '?ngModel', compile: function compile() { // Require CodeMirror if (angular.isUndefined(window.CodeMirror)) { throw new Error('ui-codemirror needs CodeMirror to work... (o rly?)'); } return postLink; } }; function postLink(scope, iElement, iAttrs, ngModel) { var codemirrorOptions = angular.extend( { value: iElement.text() }, uiCodemirrorConfig.codemirror || {}, scope.$eval(iAttrs.uiCodemirror), scope.$eval(iAttrs.uiCodemirrorOpts) ); var codemirror = newCodemirrorEditor(iElement, codemirrorOptions); configOptionsWatcher( codemirror, iAttrs.uiCodemirror || iAttrs.uiCodemirrorOpts, scope ); configNgModelLink(codemirror, ngModel, scope); configUiRefreshAttribute(codemirror, iAttrs.uiRefresh, scope); // Allow access to the CodeMirror instance through a broadcasted event // eg: $broadcast('CodeMirror', function(cm){...}); scope.$on('CodeMirror', function(event, callback) { if (angular.isFunction(callback)) { callback(codemirror); } else { throw new Error('the CodeMirror event requires a callback function'); } }); // onLoad callback if (angular.isFunction(codemirrorOptions.onLoad)) { codemirrorOptions.onLoad(codemirror); } } function newCodemirrorEditor(iElement, codemirrorOptions) { var codemirrot; if (iElement[0].tagName === 'TEXTAREA') { // Might bug but still ... codemirrot = window.CodeMirror.fromTextArea(iElement[0], codemirrorOptions); } else { iElement.html(''); codemirrot = new window.CodeMirror(function(cm_el) { iElement.append(cm_el); }, codemirrorOptions); } return codemirrot; } function configOptionsWatcher(codemirrot, uiCodemirrorAttr, scope) { if (!uiCodemirrorAttr) { return; } var codemirrorDefaultsKeys = Object.keys(window.CodeMirror.defaults); scope.$watch(uiCodemirrorAttr, updateOptions, true); function updateOptions(newValues, oldValue) { if (!angular.isObject(newValues)) { return; } codemirrorDefaultsKeys.forEach(function(key) { if (newValues.hasOwnProperty(key)) { if (oldValue && newValues[key] === oldValue[key]) { return; } codemirrot.setOption(key, newValues[key]); } }); } } function configNgModelLink(codemirror, ngModel, scope) { if (!ngModel) { return; } // CodeMirror expects a string, so make sure it gets one. // This does not change the model. ngModel.$formatters.push(function(value) { if (angular.isUndefined(value) || value === null) { return ''; } else if (angular.isObject(value) || angular.isArray(value)) { throw new Error('ui-codemirror cannot use an object or an array as a model'); } return value; }); // Override the ngModelController $render method, which is what gets called when the model is updated. // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else. ngModel.$render = function() { //Code mirror expects a string so make sure it gets one //Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive) var safeViewValue = ngModel.$viewValue || ''; codemirror.setValue(safeViewValue); }; // Keep the ngModel in sync with changes from CodeMirror codemirror.on('change', function(instance) { var newValue = instance.getValue(); if (newValue !== ngModel.$viewValue) { scope.$evalAsync(function() { ngModel.$setViewValue(newValue); }); } }); // Fix for using codemirror in tabs (or modal window) codemirror.on('changes', function(instance, changes) { $timeout(function(){ instance.refresh(); }); }); } function configUiRefreshAttribute(codeMirror, uiRefreshAttr, scope) { if (!uiRefreshAttr) { return; } scope.$watch(uiRefreshAttr, function(newVal, oldVal) { // Skip the initial watch firing if (newVal !== oldVal) { $timeout(function() { codeMirror.refresh(); }); } }); } } uiCodemirrorDirective.$inject = ["$timeout", "uiCodemirrorConfig"]; });