123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- 'use strict';
- /**
- * Binds a CodeMirror widget to a <textarea> element.
- */
- angular.module('ui.codemirror', [])
- .constant('uiCodemirrorConfig', {})
- .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 need 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);
- });
- }
- });
- }
- 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"];
|