diff --git a/src/ui-codemirror.js b/src/ui-codemirror.js index 480f4f2..ebcf3c7 100644 --- a/src/ui-codemirror.js +++ b/src/ui-codemirror.js @@ -12,139 +12,133 @@ angular.module('ui.codemirror', []) */ function uiCodemirrorDirective($timeout, uiCodemirrorConfig) { - return { - restrict: 'EA', - require: '?ngModel', - compile: function compile() { + return { + restrict: 'EA', + require: '?ngModel', + scope: { + model: '=ngModel' + }, + link: postLink + }; - // Require CodeMirror - if (angular.isUndefined(window.CodeMirror)) { - throw new Error('ui-codemirror needs CodeMirror to work... (o rly?)'); - } + function postLink(scope, iElement, iAttrs, ngModel) { - 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); + // Require CodeMirror + if (angular.isUndefined(window.CodeMirror)) { + throw new Error('ui-codemirror needs CodeMirror to work... (o rly?)'); + } + + 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); + } } - return codemirrot; - } + 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); + } - function configOptionsWatcher(codemirrot, uiCodemirrorAttr, scope) { - if (!uiCodemirrorAttr) { return; } + return codemirrot; + } - 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)) { + function configOptionsWatcher(codemirrot, uiCodemirrorAttr, scope) { + if (!uiCodemirrorAttr) { return; } - if (oldValue && newValues[key] === oldValue[key]) { - 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)) { - codemirrot.setOption(key, newValues[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); - }; + 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; + }); - // 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); + scope.$watch('model', 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 safeValue = scope.model || ''; + codemirror.setValue(safeValue); }); - } - }); - } - - 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(); + + // Keep the ngModel in sync with changes from CodeMirror + codemirror.on('change', function (instance) { + var newValue = instance.getValue(); + if (newValue !== ngModel.$viewValue) { + scope.model = newValue; + } }); - } - }); - } + } + + 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(); + }); + } + }); + } }