11'use strict' ;
22
33/**
4- * Binds a ACE Ediitor widget
4+ * Binds a ACE Editor widget
55 */
66angular . module ( 'ui.ace' , [ ] )
77 . constant ( 'uiAceConfig' , { } )
88 . directive ( 'uiAce' , [ 'uiAceConfig' , function ( uiAceConfig ) {
9+
910 if ( angular . isUndefined ( window . ace ) ) {
1011 throw new Error ( 'ui-ace need ace to work... (o rly?)' ) ;
1112 }
13+
14+ /**
15+ * Sets editor options such as the wrapping mode or the syntax checker.
16+ *
17+ * The supported options are:
18+ *
19+ * <ul>
20+ * <li>showGutter</li>
21+ * <li>useWrapMode</li>
22+ * <li>onLoad</li>
23+ * <li>theme</li>
24+ * <li>mode</li>
25+ * </ul>
26+ *
27+ * @param acee
28+ * @param session ACE editor session
29+ * @param {object } opts Options to be set
30+ */
31+ var setOptions = function ( acee , session , opts ) {
32+
33+ // Boolean options
34+ if ( angular . isDefined ( opts . showGutter ) ) {
35+ acee . renderer . setShowGutter ( opts . showGutter ) ;
36+ }
37+ if ( angular . isDefined ( opts . useWrapMode ) ) {
38+ session . setUseWrapMode ( opts . useWrapMode ) ;
39+ }
40+
41+ // onLoad callback
42+ if ( angular . isFunction ( opts . onLoad ) ) {
43+ opts . onLoad ( acee ) ;
44+ }
45+
46+ // Basic options
47+ if ( angular . isString ( opts . theme ) ) {
48+ acee . setTheme ( 'ace/theme/' + opts . theme ) ;
49+ }
50+ if ( angular . isString ( opts . mode ) ) {
51+ session . setMode ( 'ace/mode/' + opts . mode ) ;
52+ }
53+ }
54+
1255 return {
1356 restrict : 'EA' ,
1457 require : '?ngModel' ,
1558 link : function ( scope , elm , attrs , ngModel ) {
16- var options , opts , acee , session , onChange ;
17-
18- options = uiAceConfig . ace || { } ;
19- opts = angular . extend ( { } , options , scope . $eval ( attrs . uiAce ) ) ;
20-
21- acee = window . ace . edit ( elm [ 0 ] ) ;
22- session = acee . getSession ( ) ;
23-
24- onChange = function ( callback ) {
25- return function ( e ) {
26- var newValue = session . getValue ( ) ;
27- if ( newValue !== scope . $eval ( attrs . value ) && ! scope . $$phase && ! scope . $root . $$phase ) {
28- if ( angular . isDefined ( ngModel ) ) {
29- scope . $apply ( function ( ) {
30- ngModel . $setViewValue ( newValue ) ;
31- } ) ;
32- }
3359
34- /**
35- * Call the user onChange function.
36- */
37- if ( angular . isDefined ( callback ) ) {
38- scope . $apply ( function ( ) {
39- if ( angular . isFunction ( callback ) ) {
40- callback ( e , acee ) ;
41- }
42- else {
43- throw new Error ( 'ui-ace use a function as callback.' ) ;
44- }
45- } ) ;
46- }
47- }
48- } ;
49- } ;
60+ /**
61+ * Corresponds the uiAceConfig ACE configuration.
62+ * @type object
63+ */
64+ var options = uiAceConfig . ace || { } ;
5065
66+ /**
67+ * uiAceConfig merged with user options via json in attribute or data binding
68+ * @type object
69+ */
70+ var opts = angular . extend ( { } , options , scope . $eval ( attrs . uiAce ) ) ;
5171
52- // Boolean options
53- if ( angular . isDefined ( opts . showGutter ) ) {
54- acee . renderer . setShowGutter ( opts . showGutter ) ;
55- }
56- if ( angular . isDefined ( opts . useWrapMode ) ) {
57- session . setUseWrapMode ( opts . useWrapMode ) ;
58- }
72+ /**
73+ * ACE editor
74+ * @type object
75+ */
76+ var acee = window . ace . edit ( elm [ 0 ] ) ;
5977
60- // onLoad callback
61- if ( angular . isFunction ( opts . onLoad ) ) {
62- opts . onLoad ( acee ) ;
63- }
78+ /**
79+ * ACE editor session.
80+ * @type object
81+ * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
82+ */
83+ var session = acee . getSession ( ) ;
6484
65- // Basic options
66- if ( angular . isString ( opts . theme ) ) {
67- acee . setTheme ( 'ace/theme/' + opts . theme ) ;
68- }
69- if ( angular . isString ( opts . mode ) ) {
70- session . setMode ( 'ace/mode/' + opts . mode ) ;
71- }
85+ /**
86+ * Reference to a change listener created by the listener factory.
87+ * @function
88+ * @see listenerFactory.onChange
89+ */
90+ var onChangeListener ;
91+
92+ /**
93+ * Listener factory. Until now only change listeners can be created.
94+ * @type object
95+ */
96+ var listenerFactory = {
97+ /**
98+ * Creates a change listener which propagates the change event
99+ * to the callback from the user option onChange. It might be
100+ * exchanged during runtime, if this happens the old listener
101+ * will be unbound.
102+ *
103+ * @param callback callback function taken from the
104+ * @see onChangeListener
105+ */
106+ onChange : function ( callback ) {
107+ return function ( e ) {
108+ var newValue = session . getValue ( ) ;
109+ if ( newValue !== scope . $eval ( attrs . value ) && ! scope . $$phase && ! scope . $root . $$phase ) {
110+ if ( angular . isDefined ( ngModel ) ) {
111+ scope . $apply ( function ( ) {
112+ ngModel . $setViewValue ( newValue ) ;
113+ } ) ;
114+ }
115+
116+ /**
117+ * Call the user onChange function.
118+ */
119+ if ( angular . isDefined ( callback ) ) {
120+ scope . $apply ( function ( ) {
121+ if ( angular . isFunction ( callback ) ) {
122+ callback ( e , acee ) ;
123+ } else {
124+ throw new Error ( 'ui-ace use a function as callback.' ) ;
125+ }
126+ } ) ;
127+ }
128+ }
129+ } ;
130+ }
131+ } ;
72132
73133 attrs . $observe ( 'readonly' , function ( value ) {
74134 acee . setReadOnly ( value === 'true' ) ;
@@ -91,20 +151,36 @@ angular.module('ui.ace', [])
91151 } ;
92152 }
93153
154+ // Listen for option updates
155+ scope . $watch ( attrs . uiAce , function ( ) {
156+ opts = angular . extend ( { } , options , scope . $eval ( attrs . uiAce ) ) ;
157+
158+ // unbind old listener
159+ session . removeListener ( 'change' , onChangeListener ) ;
160+
161+ // bind new listener
162+ onChangeListener = listenerFactory . onChange ( opts . onChange ) ;
163+ session . on ( 'change' , onChangeListener ) ;
164+
165+ setOptions ( acee , session , opts ) ;
166+ } , /* deep watch */ true ) ;
167+
94168 // EVENTS
95- session . on ( 'change' , onChange ( opts . onChange ) ) ;
169+ onChangeListener = listenerFactory . onChange ( opts . onChange ) ;
170+ session . on ( 'change' , onChangeListener ) ;
96171
97- elm . on ( '$destroy' , function ( ) {
172+ elm . on ( '$destroy' , function ( ) {
98173 acee . session . $stopWorker ( ) ;
99174 acee . destroy ( ) ;
100175 } ) ;
101-
176+
102177 scope . $watch ( function ( ) {
103178 return [ elm [ 0 ] . offsetWidth , elm [ 0 ] . offsetHeight ] ;
104179 } , function ( ) {
105180 acee . resize ( ) ;
106181 acee . renderer . updateFull ( ) ;
107182 } , true ) ;
183+
108184 }
109185 } ;
110186 } ] ) ;
0 commit comments