├── .gitignore
├── .jshintrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── demo
├── demo.css
├── demo.html
└── demo.js
├── gruntFile.js
├── package.json
├── publish.js
├── src
└── ui-ace.js
└── test
├── ace.spec.js
└── karma.conf.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | dist/
4 | out/
5 | coverage/
6 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "boss": true,
3 | "browser": true,
4 | "eqnull": true,
5 | "expr": true,
6 | "globalstrict": true,
7 | "immed": true,
8 | "laxbreak": true,
9 | "loopfunc": true,
10 | "newcap": true,
11 | "noarg": true,
12 | "noempty": true,
13 | "nonew": true,
14 | "quotmark": true,
15 | "smarttabs": true,
16 | "sub": true,
17 | "trailing": true,
18 | "undef": true,
19 | "unused": true,
20 | "globals": {
21 | "angular": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | git:
3 | depth: 1
4 | language: node_js
5 | node_js:
6 | - '0.10'
7 | before_install:
8 | - export CHROME_BIN=chromium-browser
9 | - export DISPLAY=:99.0
10 | - sh -e /etc/init.d/xvfb start
11 | - npm update -g
12 | before_script:
13 | - npm install -qg bower grunt-cli
14 | - bower install
15 | after_success:
16 | - "./node_modules/angular-ui-publisher/travis/authentication.sh || exit 0"
17 | - "grunt dist build:gh-pages publish:gh-pages build:bower publish:bower"
18 | env:
19 | global:
20 | - REPO="git@github.com:angular-ui/ui-ace.git"
21 | - secure: ! 'VLH/q8Pk4ctxDAyXJ8py5H8ebfjgHPr7yGjGq3ktmKX7QPAVaeQ8RLfWgYsJ
22 |
23 | lJb8MJoorbh4gmjNnN2hooy++PoosIF9xXNyHASnfvW17cT/Ty450EQ8W0Mq
24 |
25 | 5b24H/YO58BcDnGXhJZjyiCQxD/7RqA93WvFA/TeCDwhfFsT7I0='
26 | - secure: ! 'TBUTlinCENkifp/Q2pO/dO05TpH47f2XfTcweo4AdPxb0wREqasf1uJZbfe6
27 |
28 | +i9UgtkbeLn23qUeejkROhhNj9spuwd9k3MZ/qcoESshUXQj4QSiylukmJux
29 |
30 | Mn374o0UoCoTDe533KbSCS6Gv1RYeHXlACnMN20u/efYLYbmkW4='
31 | - secure: ! 'f2a2Dw3NBwDULKIjzT73U8GNBx4K40dnHguH3oWwgQFjadImhirhUAqFH6N3
32 |
33 | 11GucoO8NQzZHvHJK4xM/1ZQh1JAqBjWCNOxdSZ81mlTa63L9/Lb5olsq3IV
34 |
35 | c5Lj/0xNWs8uaOBJWj2y/UCzHI/oN7XU0Qwnw/eDB4zNAFYnMuY='
36 | - secure: ! 'UOPDi1c8u2Tcq2kIRsVvC2bTPXepXoZnPBZFVZLPKOXjMfTd6YltU8ax4OE9
37 |
38 | et91q+DXgedscfsRk+0+S3UthKMUKx7bsUPoTOE1ZQ8r/t4NUmrxCuL7Sh6s
39 |
40 | pFElhbYDrE/cZKP9d+RaiP8gQrZLsEGiRSeDwiccHJ9UqbwaX9g='
41 | - secure: ! 'TO6Lt1P6Ovz56YlPLJEduFXWsEYk6vQ1RnK3b1tX2NNcyvPg0i9+yLa/gtvG
42 |
43 | TBJjh3lUz5osRwEKOkrNhz3n5wX6IPUXUpsO/RXSuWWyqkp2HQnF5h0V1uzw
44 |
45 | 79Ph+ZhSGla1h9RFqe+Qu7uXt56p8XGCLf0wcHpcy4qAS24ghbM='
46 | - secure: ! 'ROX/ZxF62Mh0Ibepek+tLJp6YKOcqj8nu7dxlIDZmEOk17oZvU3kY5EH+5B8
47 |
48 | nvF3htGs/fLKE/RmAMPXsN+BlCq810a0oP+cjvNG+3lasxhjnBOQ7W7JrEWn
49 |
50 | OuPsmNOoBPekpcBh6MNHQXKrZAeCrJ29vIlBjKZxANGi/w6nHww='
51 | - secure: ! 'B3ZuO6PejsK6fyFuqp9BGVuDva8GYRxJyx4WUTXxz+BsxgRm5GXon3L7caXY
52 |
53 | jffI2MkglsqCulXn2ckfq1O2KPEU41Gp9uLZJCDIMuSz+ItCXuSf4mdx0YLr
54 |
55 | P8bNPN5FwHwbXw/MI+LfF6LT+8Fm2Dj5gVl6aKW6+2/uKH8dEj0='
56 | - secure: ! 'I3eyfTEtxHKmEcOA/27h9C+nFLadrC3N3PQn1Owrb+zngFzCLF/NkTxkBA1v
57 |
58 | UiNHaB6RL08i2qJAmtpcFKHFZy+q/qaMJeaVN4rVOrOi7xHRbxTOSMp0IwyC
59 |
60 | Z8wnHQosbsMt6lYQ8CF9W7qyOfBCcUEk3R++jWRuX0CHXMyRldY='
61 | - secure: ! 'MYeCpdwZQy79+Rv3JnaI5lpuVHg5fGMRzOTpSNFE6TREgihX85s8YRH+j/ru
62 |
63 | 63mM7GHKZiTMQgblVJ073mxEzIGWwMq4+xpcQjlBcRXgb01e7tVpcj7wZe7f
64 |
65 | pLqZ7igWBFrlaRMTDfmH6JdjkZl80vGQQiBZdhzrinoWcxx+r5o='
66 | - secure: ! 'O74deugKvWkpHTqdTjwEtX3JcESIMGMxOKwzuTOKsHLOJWbs0VqUrbRcn8Zc
67 |
68 | C+gmybRF+eTcsdvd2g/1LQFmY47F20j1pOBOpzD9cLw/iFbibUF+j7fXGVhH
69 |
70 | I5hQQKoPm9Ql72kO8H4cyK2UDGceXWaS+iut69BSiI3vAp0n5DA='
71 | - secure: ! 'QBC1ePYxDDp4Y3pAZ93VYbPni3qq0TS80yC/9aDCGZftz6wDELlGRcZ8lmgE
72 |
73 | iFbkRGUcKmRmYuMweD/nWot/eXa7U3Gt6oeQHANlwwOjq+ggtDY3xGpPM2IP
74 |
75 | z4upux00abx8Iu1mdmdWoGnungCrwNna0C5Om8L+y4ItAv9s2K8='
76 | - secure: ! 'JbA01+Mky7LkH9FiRet02dd2x6V6K2glJjFByOcBit4TJ2qlJhjej2Pmb98B
77 |
78 | Szb71hz/olxaeocTJj3kSC6JTL6A692wXg/qkCa+Ns8+ZPLXZax2CMc0ROAF
79 |
80 | 3Q+WrUG6M804LqAqIyV7GLQInY+yVrQ9Q4ZQL9HM4KYxTMz24M8='
81 | - secure: ! 'QEAGBuplaQtdWAtT2LgrPaovq2XwOHKyLuNCOX15xR3lH+uP66Lu2sbo94X4
82 |
83 | tmgL0os+6pOpLN8IsDufBAqgPuL8/VY36Zh6pOz5QQ8EOGOpBRZ/UxITcLlt
84 |
85 | IoII/b1nWZspAV+o3J6ZsTvwGhj4UB6xa7wtxPDZPuQZE3r+o64='
86 | - secure: ! 'ajSiUYBFIL62qQvct9+fPlKS3RhLP+Bwe2mAQ72SNPW+qu3kPdpJjHzbsSuA
87 |
88 | KprhwGDXfDMc9qyOXEaRptmcm263Sur8+1rzI4IWJvgbk4p7yWTKRtnCAi7+
89 |
90 | rvVlw4ob3PjNaYPcbpX4x/Emt6BDoRt0vFYmGjR0uDdsmaXBJgI='
91 | - secure: ! 'cesPxKxMwhqIF2GOJHB3kJJ0QKyR6bsz+p8rv7BeOf/it/mIq2NFZUdSy7IN
92 |
93 | uEJgCvkx8fbf0nIgk1z2q6qI5nk7NYR/ukVyfKwFXViD7s2Jp0Uraeo3HWo1
94 |
95 | ktQBKoE+dDb+fDc0dnnIF5ObMBiP1TezwAjUHxTp91NltMOm6RY='
96 | - secure: ! 'geWYiP5Lfl4D1ClaQBgPrqHlVjALdM4huBOsxW32f2J9btfqceNrr8VSJ5lq
97 |
98 | +FGxE1uaScG2APz6Di6jDIs9UAoG0uaSYVaMf/O5M/VT5zX3C/uUFe0SRVjX
99 |
100 | XTw8cTOdm50C0ePNr9a47DTtMC9qZbXY++6Zj+KYeqep1M5xfkg='
101 | - secure: ! 'DhuiLpfuZRFGtLW+f3g5S5i7VME0cqC6Zvmg2njBrkQytTX7wJZr6Oq8p5Az
102 |
103 | /1IB5y4AG2RLiTakP9YpkGVoqcIecCNY3/mGsoAi4sedA/I+Qcjn1jGi7YVm
104 |
105 | rJq1DLloTIpJBtjzjqBpO13kPR/RPGAoHAnOF/pIkiVZHpOgk5c='
106 | - secure: ! 'IpPbGbS17dU7XcBNSqex8M2eTdjsQkaxV36Jb4Cc6rm49rp6yddyNeK88FOH
107 |
108 | HP4r3Cf7ANv/mI1DzMM94QBGYq9bXcA3RarSZeqCtjzQ4Z9v9hkE9vWR8bGy
109 |
110 | rMsXc/kRIkUvrw9pVc4UCKkqgwJax+WKbLMatIEVYeCTn0Nnprk='
111 | - secure: ! 'gpDRE0U7rNPZ5VIGPZ9v6dq0hmXdyqrwW5Uf26OWkI21Brpbntx6m1cksGWH
112 |
113 | 4/MT+HHacMDvnw/hFUsXG3WZKpwx6s9wh6QYERo842Vb6WlyDczCypcToQHW
114 |
115 | jzgm773MYyZ6aqF+9Z+owxFWvGdi1dLfinUuSa1pAZGLvKtkjHY='
116 | - secure: ! 'GNTtN5gLvJGB5GuR+lAVqpF46Hv+cUmS97V6e8mibhWvuJHp68Quu0AUF7c7
117 |
118 | HCk70lhGB+IR9voqjgmMmu1m5a58VJwoiqlt7lyXkhauh/J5xe2OSkYD0mBa
119 |
120 | 6IwP/XyFBalW7rhaQhH5VL6MO+S/gdNJ7Thb3z39IMC8oSDGY+8='
121 | - secure: ! 'fPrBq24xYHsQ5YsDX4HksoatIQnhw4PssD/jb4VAEtPA2Vw0eJKaBquBwoVU
122 |
123 | xhpsMmr7I1OfjPaavldMFFBH+/uMnKANQWxTrZTJ1zr8Yphsha46DuqOH60/
124 |
125 | w6NguZE7Ipdx+VfDA6WQFYGtowI02oKzWjshCrndWYw9Q7icx9o='
126 | - secure: ! 'T0f9CyHOo2y/UIk6BBmxd7cMKNYp9/FyQTfJP3YE3GVbdeZAJg4GyiD2kVWP
127 |
128 | 89kcUUoOz2MM79XB77tuH+u7I3U1HXaWwbCvxo6S5a5nn2/Y4ae06QmvcU+7
129 |
130 | Iiv9n1uLtPdxQyTBRP0Tg1TsCWVXF0/FxxnAEvTUPlraoLLzn/Q='
131 | - secure: ! 'TCxSdqLdgNFHAzscEWDTMkLTQqBh26kEsEiU3cvhbczsgIqIHPlEvT7U5+1k
132 |
133 | O1d+URqsH7560aDN5Slt1FeCQDmDZtNe+RxMDlw2HnnrZXOGDjUpUvtcZDif
134 |
135 | TZ/1xM4ZIoU/w2K0qCmTZsVm/VoNrmUrH8N+uCxH4rmgvRqD0aU='
136 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ### 0.2.3 (2015-01-29)
3 |
4 |
5 | #### Bug Fixes
6 |
7 | * another change to evalAsync
8 |
9 |
10 | ### 0.2.2 (2015-01-27)
11 |
12 |
13 | #### Bug Fixes
14 |
15 | * onLoad called in the middle of the options ([5b1a3170](http://github.com/angular-ui/ui-ace/commit/5b1a3170acd482ded223cc21f31fcbf62964f4d4))
16 | * change to evalAsync
17 |
18 |
19 | ### 0.2.1 (2015-01-11)
20 |
21 |
22 | #### Bug Fixes
23 |
24 | * bad user callback digestion ([df526049](http://github.com/angular-ui/ui-ace/commit/df5260499ee83acd14fd2fd513f5fc19cec51f11))
25 |
26 |
27 |
28 | ### 0.2.0 (2015-01-08)
29 |
30 |
31 | #### Bug Fixes
32 |
33 | * ngModel put back the hack ([415610f0](http://github.com/angular-ui/ui-ace/commit/415610f0dcdc1116267e6aa4045bd5ae356f1fdc))
34 | * ngModel update ([491369a9](http://github.com/angular-ui/ui-ace/commit/491369a9d508c28ad880ad707e35c3d827c0a71a), closes [#77](http://github.com/angular-ui/ui-ace/issues/77))
35 |
36 |
37 | #### Features
38 |
39 | * readonly ng ready ([0d99e5fd](http://github.com/angular-ui/ui-ace/commit/0d99e5fd65a9870617b9010111b72f789e276c8b))
40 | * add advancedRenderer options ([76fc37d1](http://github.com/angular-ui/ui-ace/commit/76fc37d176ffe1e62dc72da4e85c027446794a64))
41 | * **config:** always call the global onLoad handler ([c83086ac](http://github.com/angular-ui/ui-ace/commit/c83086ac7be7ff08dd632ab6705c43ba39ca6867))
42 | * **directive:**
43 | * worker path for concatenated and minified configurations ([b7e20c1a](http://github.com/angular-ui/ui-ace/commit/b7e20c1aa6c4871a62b48fbb36a7a6794809d2b0))
44 | * require and advanced options ([6e160cb6](http://github.com/angular-ui/ui-ace/commit/6e160cb6f12b11365e8480509332951ea43d7e5a))
45 |
46 |
47 |
48 | ### 0.1.2 (2015-01-08)
49 |
50 |
51 |
52 | ### 0.1.1 (2015-01-08)
53 |
54 |
55 | #### Bug Fixes
56 |
57 | * **ace:** resize editor when its width/height changes ([b2024c14](http://github.com/angular-ui/ui-ace/commit/b2024c14dc6336bc232e1bb0b124b6be26f2bbee), closes [#26](http://github.com/angular-ui/ui-ace/issues/26))
58 |
59 |
60 |
61 | ## v0.1.0 (2013-12-28)
62 |
63 |
64 | #### Bug Fixes
65 |
66 | * **publisher:** remove typo ([15a99e22](http://github.com/angular-ui/ui-ace/commit/15a99e22d4b761845abc7e7644b88d7eb45ee538))
67 | * **travis:**
68 | * use node 0.10 ([588f6f1f](http://github.com/angular-ui/ui-ace/commit/588f6f1fc6fbca76b82db4e1e1c0a1d34d2a9835))
69 | * use angular-ui-publisher ([126de946](http://github.com/angular-ui/ui-ace/commit/126de946574f857919010bf3e2f7e46f52629b23))
70 | * **ui-ace:** call $destroy when removed ([edb4fa14](http://github.com/angular-ui/ui-ace/commit/edb4fa149b8d2c9dbb7314a69e5d58dbc688fc0d))
71 |
72 |
73 | ### v0.0.5 (2013-12-14)
74 |
75 |
76 | #### Bug Fixes
77 |
78 | * **demo:** Correct indentation. ([3482eaa2](http://github.com/angular-ui/ui-ace/commit/3482eaa2b570e6818e652d4ce116974511b8732c))
79 | * **travis:**
80 | * Run bower install twice to make sure it does ([6c909f6b](http://github.com/angular-ui/ui-ace/commit/6c909f6b444f7d1ce67f1c4e7e0245ba83c75700))
81 | * Travis scripts bug... ([bbae258e](http://github.com/angular-ui/ui-ace/commit/bbae258e30c8a87fa3694422207b20a750729177))
82 | * **ui-ace:** issue with digest already in progress error ([1b2dcd51](http://github.com/angular-ui/ui-ace/commit/1b2dcd516e430915f698d574513f93bb1bce4b68))
83 |
84 |
85 | #### Features
86 |
87 | * **demo:**
88 | * Add more demos ! ([5a2be000](http://github.com/angular-ui/ui-ace/commit/5a2be000fae6936a5a260e636feb9674de2e782e))
89 | * Add a demo.css file ([83bbacd5](http://github.com/angular-ui/ui-ace/commit/83bbacd54cbabb7eb7a9b072f09c589251db57c7))
90 | * Add angular-ui-bootstrap-bower for tabset directive. ([df6a1b67](http://github.com/angular-ui/ui-ace/commit/df6a1b67067105220e7e4b0b600275653a97f47a))
91 | * **test:** add test code for the readOnly option ([5da05ab7](http://github.com/angular-ui/ui-ace/commit/5da05ab7b3f8a45f06cdc0e19655e7efcffdeb64))
92 | * **ui-ace:**
93 | * Make the readonly option use double-curly expressions. Close #3. ([2115d615](http://github.com/angular-ui/ui-ace/commit/2115d61529bd4f9ec4db4f95a414ebd2396ef7ad))
94 | * add readOnly option ([cffd1e45](http://github.com/angular-ui/ui-ace/commit/cffd1e454ebcf24ebafa18830e20ab8ef4f5c27e))
95 |
96 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2012 the AngularUI Team, http://angular-ui.github.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UI.Ace directive [](https://travis-ci.org/angular-ui/ui-ace)
2 |
3 | This directive allows you to add [ACE](http://ajaxorg.github.io/ace/) editor elements.
4 |
5 | ## Requirements
6 |
7 | - AngularJS
8 | - [Ace 1.x](https://github.com/ajaxorg/ace-builds/)
9 |
10 |
11 | ## Usage
12 |
13 | You can get it from [Bower](http://bower.io/)
14 |
15 | ```sh
16 | bower install angular-ui-ace#bower
17 | ```
18 |
19 | This will copy the UI.Ace files into a `bower_components` folder, along with its dependencies. Load the script files in your application:
20 |
21 | ```html
22 |
23 |
24 |
25 | ```
26 |
27 | Add the UI.Ace module as a dependency to your application module:
28 |
29 | ```javascript
30 | var myAppModule = angular.module('MyApp', ['ui.ace']);
31 | ```
32 |
33 | Finally, add the directive to your html:
34 |
35 | ```html
36 |
37 | ```
38 |
39 | To see something it's better to add some CSS, like
40 |
41 |
42 | ```css
43 | .ace_editor { height: 200px; }
44 | ```
45 |
46 | ## Options
47 |
48 | Ace doesn't provide a one gate access to all the options the jquery way.
49 | Each option is configured with the function of a specific instance.
50 | See the [api doc](http://ajaxorg.github.io/ace/#nav=api) for more.
51 |
52 | Although, _ui-ace_ automatically handles some handy options :
53 | + _showGutter_ : to show the gutter or not.
54 | + _useWrapMode_ : to set whether or not line wrapping is enabled.
55 | + _theme_ : to set the theme to use.
56 | + _mode_ : to set the mode to use.
57 | + _onLoad_ : callback when the editor has finished loading (see [below](#ace-instance-direct-access)).
58 | + _onChange_ : callback when the editor content is changed ().
59 | + _onBlur_ : callback when the editor is blurred ().
60 | + _firstLineNumber_ : to set the firstLineNumber (default: 1)
61 |
62 | ```html
63 |
72 | ```
73 |
74 | You'll want to define the `onLoad` and the `onChange` callback on your scope:
75 |
76 | ```javascript
77 | myAppModule.controller('MyController', [ '$scope', function($scope) {
78 |
79 | $scope.aceLoaded = function(_editor) {
80 | // Options
81 | _editor.setReadOnly(true);
82 | };
83 |
84 | $scope.aceChanged = function(e) {
85 | //
86 | };
87 |
88 | }]);
89 | ```
90 |
91 | To handle other options you'll have to use a direct access to the Ace created instance (see [below](#ace-instance-direct-access)).
92 |
93 | ## Advanced Options
94 |
95 | You can specify advanced options and even `require` options in the directive, as well. For this example, you
96 | will have to include the `ext-language_tools.js` file from the ace source code.
97 |
98 | This will copy the UI.Ace files into a `bower_components` folder, along with its dependencies. Load the script files in your application:
99 |
100 | ```html
101 |
102 | ```
103 |
104 | ```html
105 |
113 | ```
114 |
115 | To include options applicable to the ACE renderer, you can use the `rendererOptions` key:
116 |
117 | ```html
118 |
123 | ```
124 |
125 | ## Support for concatenated bundles
126 |
127 | Trying to use ace with concatenated javascript files usually fails because it changes the physical location of the `workerPath`. If you
128 | need to work with bundled or minified versions of ace, you can specify the original location of the `workerPath` on disk (_not the bundled file_).
129 |
130 | This should be the folder on disk where `ace.js` resides.
131 |
132 | ```html
133 |
136 | ```
137 |
138 | ### Working with ng-model
139 |
140 | The ui-ace directive plays nicely with ng-model.
141 |
142 | The ng-model will be watched for to set the Ace EditSession value (by [setValue](http://ajaxorg.github.io/ace/#nav=api&api=edit_session)).
143 |
144 | _The ui-ace directive stores and expects the model value to be a standard javascript String._
145 |
146 | ### Can be read only
147 |
148 | Simple demo
149 | ```html
150 |
151 | or
152 | Check me to make Ace readonly:
153 |
154 | ```
155 |
156 | ### Ace instance direct access
157 |
158 | For more interaction with the Ace instance in the directive, we provide a direct access to it.
159 | Using
160 |
161 | ```html
162 |
163 | ```
164 |
165 | the `$scope.aceLoaded` function will be called with the [Ace Editor instance](http://ajaxorg.github.io/ace/#nav=api&api=editor) as first argument
166 |
167 | ```javascript
168 | myAppModule.controller('MyController', [ '$scope', function($scope) {
169 |
170 | $scope.aceLoaded = function(_editor){
171 | // Editor part
172 | var _session = _editor.getSession();
173 | var _renderer = _editor.renderer;
174 |
175 | // Options
176 | _editor.setReadOnly(true);
177 | _session.setUndoManager(new ace.UndoManager());
178 | _renderer.setShowGutter(false);
179 |
180 | // Events
181 | _editor.on("changeSession", function(){ ... });
182 | _session.on("change", function(){ ... });
183 | };
184 |
185 | }]);
186 | ```
187 |
188 | ## Testing
189 |
190 | We use Karma and jshint to ensure the quality of the code. The easiest way to run these checks is to use grunt:
191 |
192 | ```sh
193 | npm install -g grunt-cli
194 | npm install && bower install
195 | grunt
196 | ```
197 |
198 | The karma task will try to open Firefox and Chrome as browser in which to run the tests. Make sure this is available or change the configuration in `test\karma.conf.js`
199 |
200 |
201 | ### Grunt Serve
202 |
203 | We have one task to serve them all !
204 |
205 | ```sh
206 | grunt serve
207 | ```
208 |
209 | It's equal to run separately:
210 |
211 | * `grunt connect:server` : giving you a development server at [http://127.0.0.1:8000/](http://127.0.0.1:8000/).
212 |
213 | * `grunt karma:server` : giving you a Karma server to run tests (at [http://localhost:9876/](http://localhost:9876/) by default). You can force a test on this server with `grunt karma:unit:run`.
214 |
215 | * `grunt watch` : will automatically test your code and build your demo. You can demo generation with `grunt build:gh-pages`.
216 |
217 |
218 | ### Dist
219 |
220 | This repo is using the [angular-ui/angular-ui-publisher](https://github.com/angular-ui/angular-ui-publisher).
221 | New tags will automatically trigger a new publication.
222 | To test is locally you can trigger a :
223 |
224 | ```sh
225 | grunt dist build:bower
226 | ```
227 |
228 | it will put the final files in the _'dist'_ folder and a sample of the bower tag output in the _'out/built/bower'_ folder.
229 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-ui-ace",
3 | "version": "0.2.3",
4 | "description": "This directive allows you to add ACE editor elements.",
5 | "author": "https://github.com/angular-ui/ui-ace/graphs/contributors",
6 | "license": "MIT",
7 | "homepage": "http://angular-ui.github.com",
8 | "main": "./src/ui-ace.js",
9 | "ignore": [
10 | "**/.*",
11 | "node_modules",
12 | "bower_components",
13 | "test*",
14 | "demo*",
15 | "gruntFile.js",
16 | "package.json"
17 | ],
18 | "dependencies": {
19 | "angular": "~1.x",
20 | "ace-builds": "^1"
21 | },
22 | "devDependencies": {
23 | "angular-mocks": "~1.x"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo/demo.css:
--------------------------------------------------------------------------------
1 | .ace_editor {
2 | height: 180px;
3 | }
4 |
5 | #demo-general .ace_editor {
6 | height: 250px;
7 | }
8 |
9 | #demo-mode-changing .ace_editor {
10 | height: 300px;
11 | }
12 |
--------------------------------------------------------------------------------
/demo/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
# Theme and mode
63 |
64 | *Lorem ipsum* dolor sit amet, consectetur adipisicing elit, sed do eiusmod
65 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
66 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
67 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
68 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
69 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
70 |
71 |
72 |
73 |
74 |
79 |
80 |
83 |
84 |
85 |
86 |
87 |
88 |
<section>
89 | <div ui-ace="{
90 | useWrapMode : true,
91 | showGutter: false,
92 | theme:'twilight',
93 | mode: 'markdown'
94 | }" ># Theme and mode
95 |
96 | *Lorem ipsum* dolor sit amet, consectetur adipisicing elit, sed do eiusmod
97 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
98 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
99 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
100 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
101 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
102 |
103 | </section>
104 |
105 |
106 |
107 |
108 |
.ace_editor {
109 | height : 200px;
110 | }
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
122 |
123 |
124 |
Mode-Changing demo
125 |
126 |
127 |
128 |
129 |
Ace here
130 |
131 |
132 | Mode :
133 |
134 |
135 |
136 |
137 |
138 |
143 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
<section ng-controller="AceCtrl">
153 |
154 | <div ui-ace="aceOption" ng-model="aceModel"></div>
155 |
156 | <select ng-model="mode" ng-options="m for m in modes" ng-change="modeChanged()"></select>
157 |
158 | </section>
159 |
160 |
161 |
162 |
163 |
164 |
function AceCtrl($scope) {
165 | // The modes
166 | $scope.modes = ['Scheme', 'XML', 'Javascript'];
167 | $scope.mode = $scope.modes[0];
168 |
169 |
170 | // The ui-ace option
171 | $scope.aceOption = {
172 | mode: $scope.mode.toLowerCase(),
173 | onLoad: function (_ace) {
174 |
175 | // HACK to have the ace instance in the scope...
176 | $scope.modeChanged = function () {
177 | _ace.getSession().setMode("ace/mode/" + $scope.mode.toLowerCase());
178 | };
179 |
180 | }
181 | };
182 |
183 | // Initial code content...
184 | $scope.aceModel = ';; Scheme code in here.\n' +
185 | '(define (double x)\n\t(* x x))\n\n\n' +
186 | '<!-- XML code in here. -->\n' +
187 | '<root>\n\t<foo>\n\t</foo>\n\t<bar/>\n</root>\n\n\n' +
188 | '// Javascript code in here.\n' +
189 | 'function foo(msg) {\n\tvar r = Math.random();\n\treturn "" + r + " : " + msg;\n}';
190 |
191 | }
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 | // sets the ace worker path, if running from concatenated
34 | // or minified source
35 | if (angular.isDefined(opts.workerPath)) {
36 | var config = window.ace.require('ace/config');
37 | config.set('workerPath', opts.workerPath);
38 | }
39 | // ace requires loading
40 | if (angular.isDefined(opts.require)) {
41 | opts.require.forEach(function (n) {
42 | window.ace.require(n);
43 | });
44 | }
45 | // Boolean options
46 | if (angular.isDefined(opts.showGutter)) {
47 | acee.renderer.setShowGutter(opts.showGutter);
48 | }
49 | if (angular.isDefined(opts.useWrapMode)) {
50 | session.setUseWrapMode(opts.useWrapMode);
51 | }
52 | if (angular.isDefined(opts.showInvisibles)) {
53 | acee.renderer.setShowInvisibles(opts.showInvisibles);
54 | }
55 | if (angular.isDefined(opts.showIndentGuides)) {
56 | acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
57 | }
58 | if (angular.isDefined(opts.useSoftTabs)) {
59 | session.setUseSoftTabs(opts.useSoftTabs);
60 | }
61 | if (angular.isDefined(opts.showPrintMargin)) {
62 | acee.setShowPrintMargin(opts.showPrintMargin);
63 | }
64 |
65 | // commands
66 | if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
67 | acee.commands.addCommands([
68 | {
69 | name: 'unfind',
70 | bindKey: {
71 | win: 'Ctrl-F',
72 | mac: 'Command-F'
73 | },
74 | exec: function () {
75 | return false;
76 | },
77 | readOnly: true
78 | }
79 | ]);
80 | }
81 |
82 | // Basic options
83 | if (angular.isString(opts.theme)) {
84 | acee.setTheme('ace/theme/' + opts.theme);
85 | }
86 | if (angular.isString(opts.mode)) {
87 | session.setMode('ace/mode/' + opts.mode);
88 | }
89 | // Advanced options
90 | if (angular.isDefined(opts.firstLineNumber)) {
91 | if (angular.isNumber(opts.firstLineNumber)) {
92 | session.setOption('firstLineNumber', opts.firstLineNumber);
93 | } else if (angular.isFunction(opts.firstLineNumber)) {
94 | session.setOption('firstLineNumber', opts.firstLineNumber());
95 | }
96 | }
97 |
98 | // advanced options
99 | var key, obj;
100 | if (angular.isDefined(opts.advanced)) {
101 | for (key in opts.advanced) {
102 | // create a javascript object with the key and value
103 | obj = { name: key, value: opts.advanced[key] };
104 | // try to assign the option to the ace editor
105 | acee.setOption(obj.name, obj.value);
106 | }
107 | }
108 |
109 | // advanced options for the renderer
110 | if (angular.isDefined(opts.rendererOptions)) {
111 | for (key in opts.rendererOptions) {
112 | // create a javascript object with the key and value
113 | obj = { name: key, value: opts.rendererOptions[key] };
114 | // try to assign the option to the ace editor
115 | acee.renderer.setOption(obj.name, obj.value);
116 | }
117 | }
118 |
119 | // onLoad callbacks
120 | angular.forEach(opts.callbacks, function (cb) {
121 | if (angular.isFunction(cb)) {
122 | cb(acee);
123 | }
124 | });
125 | };
126 |
127 | return {
128 | restrict: 'EA',
129 | require: '?ngModel',
130 | link: function (scope, elm, attrs, ngModel) {
131 |
132 | /**
133 | * Corresponds the uiAceConfig ACE configuration.
134 | * @type object
135 | */
136 | var options = uiAceConfig.ace || {};
137 |
138 | /**
139 | * uiAceConfig merged with user options via json in attribute or data binding
140 | * @type object
141 | */
142 | var opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
143 |
144 | /**
145 | * ACE editor
146 | * @type object
147 | */
148 | var acee = window.ace.edit(elm[0]);
149 |
150 | /**
151 | * ACE editor session.
152 | * @type object
153 | * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
154 | */
155 | var session = acee.getSession();
156 |
157 | /**
158 | * Reference to a change listener created by the listener factory.
159 | * @function
160 | * @see listenerFactory.onChange
161 | */
162 | var onChangeListener;
163 |
164 | /**
165 | * Reference to a blur listener created by the listener factory.
166 | * @function
167 | * @see listenerFactory.onBlur
168 | */
169 | var onBlurListener;
170 |
171 | /**
172 | * Calls a callback by checking its existing. The argument list
173 | * is variable and thus this function is relying on the arguments
174 | * object.
175 | * @throws {Error} If the callback isn't a function
176 | */
177 | var executeUserCallback = function () {
178 |
179 | /**
180 | * The callback function grabbed from the array-like arguments
181 | * object. The first argument should always be the callback.
182 | *
183 | * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
184 | * @type {*}
185 | */
186 | var callback = arguments[0];
187 |
188 | /**
189 | * Arguments to be passed to the callback. These are taken
190 | * from the array-like arguments object. The first argument
191 | * is stripped because that should be the callback function.
192 | *
193 | * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
194 | * @type {Array}
195 | */
196 | var args = Array.prototype.slice.call(arguments, 1);
197 |
198 | if (angular.isDefined(callback)) {
199 | scope.$evalAsync(function () {
200 | if (angular.isFunction(callback)) {
201 | callback(args);
202 | } else {
203 | throw new Error('ui-ace use a function as callback.');
204 | }
205 | });
206 | }
207 | };
208 |
209 | /**
210 | * Listener factory. Until now only change listeners can be created.
211 | * @type object
212 | */
213 | var listenerFactory = {
214 | /**
215 | * Creates a change listener which propagates the change event
216 | * and the editor session to the callback from the user option
217 | * onChange. It might be exchanged during runtime, if this
218 | * happens the old listener will be unbound.
219 | *
220 | * @param callback callback function defined in the user options
221 | * @see onChangeListener
222 | */
223 | onChange: function (callback) {
224 | return function (e) {
225 | var newValue = session.getValue();
226 |
227 | if (ngModel && newValue !== ngModel.$viewValue &&
228 | // HACK make sure to only trigger the apply outside of the
229 | // digest loop 'cause ACE is actually using this callback
230 | // for any text transformation !
231 | !scope.$$phase && !scope.$root.$$phase) {
232 | scope.$evalAsync(function () {
233 | ngModel.$setViewValue(newValue);
234 | });
235 | }
236 |
237 | executeUserCallback(callback, e, acee);
238 | };
239 | },
240 | /**
241 | * Creates a blur listener which propagates the editor session
242 | * to the callback from the user option onBlur. It might be
243 | * exchanged during runtime, if this happens the old listener
244 | * will be unbound.
245 | *
246 | * @param callback callback function defined in the user options
247 | * @see onBlurListener
248 | */
249 | onBlur: function (callback) {
250 | return function () {
251 | executeUserCallback(callback, acee);
252 | };
253 | }
254 | };
255 |
256 | attrs.$observe('readonly', function (value) {
257 | acee.setReadOnly(!!value || value === '');
258 | });
259 |
260 | // Value Blind
261 | if (ngModel) {
262 | ngModel.$formatters.push(function (value) {
263 | if (angular.isUndefined(value) || value === null) {
264 | return '';
265 | }
266 | else if (angular.isObject(value) || angular.isArray(value)) {
267 | throw new Error('ui-ace cannot use an object or an array as a model');
268 | }
269 | return value;
270 | });
271 |
272 | ngModel.$render = function () {
273 | session.setValue(ngModel.$viewValue);
274 | };
275 | }
276 |
277 | // Listen for option updates
278 | var updateOptions = function (current, previous) {
279 | if (current === previous) return;
280 | opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
281 |
282 | opts.callbacks = [ opts.onLoad ];
283 | if (opts.onLoad !== options.onLoad) {
284 | // also call the global onLoad handler
285 | opts.callbacks.unshift(options.onLoad);
286 | }
287 |
288 | // EVENTS
289 |
290 | // unbind old change listener
291 | session.removeListener('change', onChangeListener);
292 |
293 | // bind new change listener
294 | onChangeListener = listenerFactory.onChange(opts.onChange);
295 | session.on('change', onChangeListener);
296 |
297 | // unbind old blur listener
298 | //session.removeListener('blur', onBlurListener);
299 | acee.removeListener('blur', onBlurListener);
300 |
301 | // bind new blur listener
302 | onBlurListener = listenerFactory.onBlur(opts.onBlur);
303 | acee.on('blur', onBlurListener);
304 |
305 | setOptions(acee, session, opts);
306 | };
307 |
308 | scope.$watch(attrs.uiAce, updateOptions, /* deep watch */ true);
309 |
310 | // set the options here, even if we try to watch later, if this
311 | // line is missing things go wrong (and the tests will also fail)
312 | updateOptions(options);
313 |
314 | elm.on('$destroy', function () {
315 | acee.session.$stopWorker();
316 | acee.destroy();
317 | });
318 |
319 | scope.$watch(function() {
320 | return [elm[0].offsetWidth, elm[0].offsetHeight];
321 | }, function() {
322 | acee.resize();
323 | acee.renderer.updateFull();
324 | }, true);
325 |
326 | }
327 | };
328 | }]);
329 |
--------------------------------------------------------------------------------
/test/ace.spec.js:
--------------------------------------------------------------------------------
1 | describe('uiAce', function () {
2 | 'use strict';
3 |
4 | var scope, $compile,
5 | uiConfig;
6 |
7 | beforeEach(function () {
8 |
9 | module('ui.ace');
10 |
11 | inject(function (_$rootScope_, _$compile_, uiAceConfig) {
12 | scope = _$rootScope_.$new();
13 | $compile = _$compile_;
14 | uiConfig = uiAceConfig;
15 | uiConfig.ace = { showGutter: false };
16 | });
17 | });
18 |
19 | afterEach(function () {
20 | uiConfig = {};
21 | });
22 |
23 | describe('require', function () {
24 | var aceRequireFunction;
25 |
26 | beforeEach(function () {
27 | aceRequireFunction = window.ace.edit;
28 | window.ace.require = jasmine
29 | .createSpy('window.ace.require');
30 | });
31 |
32 | afterEach(function () {
33 | window.ace.require = aceRequireFunction;
34 | });
35 |
36 | it('should not call window.ace.require if there is no "require" option', function () {
37 | $compile('
')(scope);
38 | expect(window.ace.require).not.toHaveBeenCalled();
39 | });
40 |
41 | it('should not call ace/config if a workerPath is not defined', function () {
42 | $compile('
')(scope);
43 | expect(window.ace.require).not.toHaveBeenCalledWith('ace/config');
44 | });
45 |
46 | it('should call ace/config if a workerPath is defined', function () {
47 | window.ace.require
48 | .and.returnValue({
49 | set: function () {}
50 | });
51 | ////
52 | $compile('
')(scope);
53 | expect(window.ace.require).toHaveBeenCalledWith('ace/config');
54 | });
55 |
56 | it('should call "set" if workerPath is defined', function () {
57 | var _config = jasmine.createSpyObj('config', ['set']);
58 | window.ace.require.and.returnValue(_config);
59 | ////
60 | $compile('
')(scope);
61 | expect(_config.set).toHaveBeenCalled();
62 | });
63 |
64 | it('should call "window.ace.require" for each option in "require"', function () {
65 | $compile('