diff options
Diffstat (limited to 'js/vendor/angular-ui/modules/directives/codemirror')
3 files changed, 245 insertions, 0 deletions
diff --git a/js/vendor/angular-ui/modules/directives/codemirror/codemirror.js b/js/vendor/angular-ui/modules/directives/codemirror/codemirror.js new file mode 100644 index 000000000..403c1cc48 --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/codemirror/codemirror.js @@ -0,0 +1,77 @@ +/*global angular, CodeMirror, Error*/ +/** + * Binds a CodeMirror widget to a <textarea> element. + */ +angular.module('ui.directives').directive('uiCodemirror', ['ui.config', '$timeout', function (uiConfig, $timeout) { + 'use strict'; + + var events = ["cursorActivity", "viewportChange", "gutterClick", "focus", "blur", "scroll", "update"]; + return { + restrict:'A', + require:'ngModel', + link:function (scope, elm, attrs, ngModel) { + var options, opts, onChange, deferCodeMirror, codeMirror; + + if (elm[0].type !== 'textarea') { + throw new Error('uiCodemirror3 can only be applied to a textarea element'); + } + + options = uiConfig.codemirror || {}; + opts = angular.extend({}, options, scope.$eval(attrs.uiCodemirror)); + + onChange = function (aEvent) { + return function (instance, changeObj) { + var newValue = instance.getValue(); + if (newValue !== ngModel.$viewValue) { + ngModel.$setViewValue(newValue); + scope.$apply(); + } + if (typeof aEvent === "function") + aEvent(instance, changeObj); + }; + }; + + deferCodeMirror = function () { + codeMirror = CodeMirror.fromTextArea(elm[0], opts); + codeMirror.on("change", onChange(opts.onChange)); + + for (var i = 0, n = events.length, aEvent; i < n; ++i) { + aEvent = opts["on" + events[i].charAt(0).toUpperCase() + events[i].slice(1)]; + if (aEvent === void 0) continue; + if (typeof aEvent !== "function") continue; + codeMirror.on(events[i], aEvent); + } + + // 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 () { + codeMirror.setValue(ngModel.$viewValue); + }; + + // Watch ui-refresh and refresh the directive + if (attrs.uiRefresh) { + scope.$watch(attrs.uiRefresh, function(newVal, oldVal){ + // Skip the initial watch firing + if (newVal !== oldVal) + $timeout(codeMirror.refresh); + }); + } + }; + + $timeout(deferCodeMirror); + + } + }; +}]); diff --git a/js/vendor/angular-ui/modules/directives/codemirror/dependencies.json b/js/vendor/angular-ui/modules/directives/codemirror/dependencies.json new file mode 100644 index 000000000..30dd1ff35 --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/codemirror/dependencies.json @@ -0,0 +1,8 @@ +{ + "core": [ "jquery" ], + "internal": [], + "external": [ + "http://codemirror.net/lib/codemirror.js", + "http://codemirror.net/lib/codemirror.css", + ] +}
\ No newline at end of file diff --git a/js/vendor/angular-ui/modules/directives/codemirror/test/codemirrorSpec.js b/js/vendor/angular-ui/modules/directives/codemirror/test/codemirrorSpec.js new file mode 100644 index 000000000..5e6bf3d37 --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/codemirror/test/codemirrorSpec.js @@ -0,0 +1,160 @@ +/*global beforeEach, afterEach, describe, it, inject, expect, module, spyOn, CodeMirror, angular, $*/ +/** + * TODO Test all the CodeMirror events : cursorActivity viewportChange gutterClick focus blur scroll update. + * with <textarea ui-codemirror="{onChange: doChange ,onCursorActivity: doSomething}" ng-model="foo"> + * + */ +describe('uiCodemirror', function () { + 'use strict'; + + // declare these up here to be global to all tests + var scope, $compile, $timeout, uiConfig = angular.module('ui.config'); + + beforeEach(module('ui.directives')); + beforeEach(function () { + uiConfig.value('ui.config', {codemirror: {bar: 'baz'}}); + }); + + // inject in angular constructs. Injector knows about leading/trailing underscores and does the right thing + // otherwise, you would need to inject these into each test + beforeEach(inject(function (_$rootScope_, _$compile_, _$timeout_) { + scope = _$rootScope_.$new(); + $compile = _$compile_; + $timeout = _$timeout_; + })); + + afterEach(function () { + uiConfig.value('ui.config', {}); // cleanup + }); + + describe('compiling this directive', function () { + it('should throw an error if used against a non-textarea', function () { + function compile() { + $compile('<div ui-codemirror ng-model="foo"></div>')(scope); + } + + expect(compile).toThrow(); + }); + + it('should not throw an error when used against a textarea', function () { + function compile() { + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + } + + expect(compile).not.toThrow(); + }); + + it('should throw an error when no ngModel attribute defined', function () { + function compile() { + $compile('<textarea ui-codemirror></textarea>')(scope); + } + + expect(compile).toThrow(); + }); + + it('should watch the uiCodemirror attribute', function () { + spyOn(scope, '$watch'); + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + $timeout.flush(); + expect(scope.$watch).toHaveBeenCalled(); + }); + + }); + + describe('while spying on the CodeMirror instance', function () { + + var codemirror; + + beforeEach(function () { + var fromTextArea = CodeMirror.fromTextArea; + spyOn(CodeMirror, 'fromTextArea').andCallFake(function () { + codemirror = fromTextArea.apply(this, arguments); + return codemirror; + }); + }); + + describe('verify the directive options', function () { + it('should include the passed options', function () { + $compile('<textarea ui-codemirror="{oof: \'baar\'}" ng-model="foo"></textarea>')(scope); + $timeout.flush(); + expect(CodeMirror.fromTextArea.mostRecentCall.args[1].oof).toEqual("baar"); + }); + + it('should include the default options', function () { + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + $timeout.flush(); + expect(CodeMirror.fromTextArea.mostRecentCall.args[1].bar).toEqual('baz'); + }); + }); + + describe('when uiRefresh is added', function () { + it('should trigger the CodeMirror.refresh() method', function () { + $compile('<textarea ui-codemirror ng-model="foo" ui-refresh="bar"></textarea>')(scope); + $timeout.flush(); + spyOn(codemirror, 'refresh'); + scope.$apply('bar = true'); + $timeout.flush(); + expect(codemirror.refresh).toHaveBeenCalled(); + }); + }); + + + describe('when the IDE changes', function () { + it('should update the model', function () { + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + scope.$apply("foo = 'bar'"); + $timeout.flush(); + var value = 'baz'; + codemirror.setValue(value); + expect(scope.foo).toBe(value); + }); + }); + + describe('when the model changes', function () { + it('should update the IDE', function () { + var element = $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + scope.foo = 'bar'; + scope.$apply(); + $timeout.flush(); + expect(codemirror.getValue()).toBe(scope.foo); + }); + }); + + describe('when the model is undefined/null', function () { + it('should update the IDE with an empty string', function () { + var element = $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + scope.$apply(); + $timeout.flush(); + expect(scope.foo).toBe(undefined); + expect(codemirror.getValue()).toBe(''); + scope.$apply('foo = "bar"'); + expect(scope.foo).toBe('bar'); + expect(codemirror.getValue()).toBe('bar'); + scope.$apply('foo = null'); + expect(scope.foo).toBe(null); + expect(codemirror.getValue()).toBe(''); + }); + }); + }); + + describe('when the model is an object or an array', function () { + it('should throw an error', function () { + function compileWithObject() { + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + $timeout.flush(); + scope.foo = {}; + scope.$apply(); + } + + function compileWithArray() { + $compile('<textarea ui-codemirror ng-model="foo"></textarea>')(scope); + $timeout.flush(); + scope.foo = []; + scope.$apply(); + } + + expect(compileWithObject).toThrow(); + expect(compileWithArray).toThrow(); + }); + }); +});
\ No newline at end of file |