ember_reopen.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. /**
  19. Merge the contents of two objects together into the first object.
  20. ```javascript
  21. Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
  22. var a = {first: 'Yehuda'}, b = {last: 'Katz'};
  23. Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'}
  24. ```
  25. @method merge
  26. @for Ember
  27. @param {Object} original The object to merge into
  28. @param {Object} updates The object to copy properties from
  29. @return {Object}
  30. */
  31. Ember.merge = function(original, updates) {
  32. for (var prop in updates) {
  33. if (!updates.hasOwnProperty(prop)) { continue; }
  34. original[prop] = updates[prop];
  35. }
  36. return original;
  37. };
  38. /**
  39. Returns true if the passed value is null or undefined. This avoids errors
  40. from JSLint complaining about use of ==, which can be technically
  41. confusing.
  42. ```javascript
  43. Ember.isNone(); // true
  44. Ember.isNone(null); // true
  45. Ember.isNone(undefined); // true
  46. Ember.isNone(''); // false
  47. Ember.isNone([]); // false
  48. Ember.isNone(function() {}); // false
  49. ```
  50. @method isNone
  51. @for Ember
  52. @param {Object} obj Value to test
  53. @return {Boolean}
  54. */
  55. Ember.isNone = function(obj) {
  56. return obj === null || obj === undefined;
  57. };
  58. /**
  59. Verifies that a value is `null` or an empty string, empty array,
  60. or empty function.
  61. Constrains the rules on `Ember.isNone` by returning false for empty
  62. string and empty arrays.
  63. ```javascript
  64. Ember.isEmpty(); // true
  65. Ember.isEmpty(null); // true
  66. Ember.isEmpty(undefined); // true
  67. Ember.isEmpty(''); // true
  68. Ember.isEmpty([]); // true
  69. Ember.isEmpty('Adam Hawkins'); // false
  70. Ember.isEmpty([0,1,2]); // false
  71. ```
  72. @method isEmpty
  73. @for Ember
  74. @param {Object} obj Value to test
  75. @return {Boolean}
  76. */
  77. Ember.isEmpty = function(obj) {
  78. return Ember.isNone(obj) || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && Ember.get(obj, 'length') === 0);
  79. };
  80. /**
  81. A value is blank if it is empty or a whitespace string.
  82. ```javascript
  83. Ember.isBlank(); // true
  84. Ember.isBlank(null); // true
  85. Ember.isBlank(undefined); // true
  86. Ember.isBlank(''); // true
  87. Ember.isBlank([]); // true
  88. Ember.isBlank('\n\t'); // true
  89. Ember.isBlank(' '); // true
  90. Ember.isBlank({}); // false
  91. Ember.isBlank('\n\t Hello'); // false
  92. Ember.isBlank('Hello world'); // false
  93. Ember.isBlank([1,2,3]); // false
  94. ```
  95. @method isBlank
  96. @for Ember
  97. @param {Object} obj Value to test
  98. @return {Boolean}
  99. */
  100. Ember.isBlank = function(obj) {
  101. return Ember.isEmpty(obj) || (typeof obj === 'string' && obj.match(/\S/) === null);
  102. };
  103. /**
  104. * Calculates sum of two numbers
  105. * Use this function as a callback on <code>invoke</code> etc
  106. *
  107. * @method sum
  108. * @param {Number} a
  109. * @param {Number} b
  110. * @returns {Number}
  111. */
  112. Ember.sum = function (a, b) {
  113. return a + b;
  114. };
  115. /**
  116. * Execute passed callback
  117. *
  118. * @param {Function} callback
  119. * @returns {*}
  120. */
  121. Ember.clb = function (callback) {
  122. return callback();
  123. };
  124. /**
  125. *
  126. */
  127. Ember.RadioButton = Ember.Checkbox.extend({
  128. tagName: "input",
  129. type: "radio",
  130. attributeBindings: [ "type", "name", "value", "checked", "style", "disabled" ],
  131. style: "vertical-align: middle; margin: 0px;",
  132. click: function () {
  133. this.set("selection", this.$().val())
  134. },
  135. checked: function () {
  136. return this.get("value") == this.get("selection");
  137. }.property('value', 'selection')
  138. });
  139. /**
  140. * Set value to obj by path
  141. * Create nested objects if needed
  142. * Example:
  143. * <code>
  144. * var a = {b: {}};
  145. * var path = 'b.c.d';
  146. * var value = 1;
  147. * Em.setFullPath(a, path, value); // a = {b: {c: {d: 1}}}
  148. * </code>
  149. *
  150. * @param {object} obj
  151. * @param {string} path
  152. * @param {*} value
  153. */
  154. Ember.setFullPath = function (obj, path, value) {
  155. var parts = path.split('.'),
  156. sub_path = '';
  157. parts.forEach(function(_path, _index) {
  158. Em.assert('path parts can\'t be empty', _path.length);
  159. sub_path += '.' + _path;
  160. if (_index === parts.length - 1) {
  161. Em.set(obj, sub_path, value);
  162. return;
  163. }
  164. if (Em.isNone(Em.get(obj, sub_path))) {
  165. Em.set(obj, sub_path, {});
  166. }
  167. });
  168. };
  169. /**
  170. *
  171. * @param {object} target
  172. * @param {string[]} propertyNames
  173. * @returns {{}}
  174. */
  175. Ember.getProperties = function (target, propertyNames) {
  176. var ret = {};
  177. for(var i = 0; i < propertyNames.length; i++) {
  178. ret[propertyNames[i]] = Em.get(target, propertyNames[i]);
  179. }
  180. return ret;
  181. };
  182. Em.View.reopen({
  183. /**
  184. * overwritten set method of Ember.View to avoid uncaught errors
  185. * when trying to set property of destroyed view
  186. */
  187. set: function(attr, value){
  188. if(!this.get('isDestroyed') && !this.get('isDestroying')){
  189. this._super(attr, value);
  190. } else {
  191. console.debug('Calling set on destroyed view');
  192. }
  193. },
  194. /**
  195. * overwritten setProperties method of Ember.View to avoid uncaught errors
  196. * when trying to set multiple properties of destroyed view
  197. */
  198. setProperties: function(hash){
  199. if(!this.get('isDestroyed') && !this.get('isDestroying')){
  200. this._super(hash);
  201. } else {
  202. console.debug('Calling setProperties on destroyed view');
  203. }
  204. }
  205. });
  206. Ember._HandlebarsBoundView.reopen({
  207. /**
  208. * overwritten set method of Ember._HandlebarsBoundView to avoid uncaught errors
  209. * when trying to set property of destroyed view
  210. */
  211. render: function(buffer){
  212. if(!this.get('isDestroyed') && !this.get('isDestroying')){
  213. this._super(buffer);
  214. } else {
  215. console.debug('Calling set on destroyed view');
  216. }
  217. }
  218. });
  219. Ember.TextArea.reopen({
  220. attributeBindings: ['readonly']
  221. });
  222. /**
  223. * Simply converts query string to object.
  224. *
  225. * @param {string} queryString query string e.g. '?param1=value1&param2=value2'
  226. * @return {object} converted object
  227. */
  228. function parseQueryParams(queryString) {
  229. if (!queryString) {
  230. return {};
  231. }
  232. return queryString.replace(/^\?/, '').split('&').map(decodeURIComponent)
  233. .reduce(function(p, c) {
  234. var keyVal = c.split('=');
  235. p[keyVal[0]] = keyVal[1];
  236. return p;
  237. }, {});
  238. };
  239. Ember.Route.reopen({
  240. /**
  241. * When you move to a new route by pressing the back or forward button, change url manually, click on link with url defined in href,
  242. * call Router.transitionTo or Router.route this method is called.
  243. * This method unites unroutePath, navigateAway and exit events to handle Route leaving in one place.
  244. * Also unlike the exit event it is possible to stop transition inside this handler.
  245. * To proceed transition just call callback..
  246. *
  247. * @param {Router} router
  248. * @param {Object|String} context context from transition or path from route
  249. * @param {callback} callback should be called to proceed transition
  250. */
  251. exitRoute: function (router, context, callback) {
  252. callback();
  253. },
  254. /**
  255. * Query Params serializer. This method should be used inside <code>serialize</code> method.
  256. * You need to specify `:query` dynamic sygment in your route's <code>route</code> attribute
  257. * e.g. Em.Route.extend({ route: '/login:query'}) and return result of this method.
  258. * This method will set <code>serializedQuery</code> property to specified controller by name.
  259. * For concrete example see `app/routes/main.js`.
  260. *
  261. * @example
  262. * queryParams: Em.Route.extend({
  263. * route: '/queryDemo:query',
  264. * serialize: function(route, params) {
  265. * return this.serializeQueryParams(route, params, 'controllerNameToSetQueryObject');
  266. * }
  267. * });
  268. * // now when navigated to http://example.com/#/queryDemo?param1=value1&param2=value2
  269. * // App.router.get('controllerNameToSetQueryObject').get('serializedQuery')
  270. * // will return { param1: 'value1', param2: 'value2' }
  271. *
  272. * @param {Em.Router} router router instance passed to <code>serialize</code> method
  273. * @param {object} params dynamic segment passed to <code>seriazlie</code>
  274. * @param {string} controllerName name of the controller to set `serializedQuery` as result
  275. * @return {object}
  276. */
  277. serializeQueryParams: function(router, params, controllerName) {
  278. var controller = router.get(controllerName);
  279. controller.set('serializedQuery', parseQueryParams(params ? params.query : ''));
  280. return params || { query: ''};
  281. }
  282. });
  283. Ember.Router.reopen({
  284. // reopen original transitionTo and route methods to add calling of exitRoute
  285. transitionTo: function (router, context) {
  286. var self = this;
  287. var args = arguments;
  288. var transitionTo = self._super;
  289. var callback = function () {
  290. transitionTo.apply(self, args);
  291. };
  292. if (!this.get('currentState.exitRoute')) {
  293. callback();
  294. } else {
  295. this.get('currentState').exitRoute(this, context, callback);
  296. }
  297. },
  298. route: function (path) {
  299. var self = this;
  300. var args = arguments;
  301. var transitionTo = self._super;
  302. var callback = function () {
  303. self.get('location').setURL(path);
  304. transitionTo.apply(self, args);
  305. };
  306. var realPath;
  307. if (!this.get('currentState.exitRoute')) {
  308. callback();
  309. } else {
  310. realPath = this.get('currentState').absoluteRoute(this);
  311. this.get('location').setURL(realPath);
  312. this.get('currentState').exitRoute(this, path, callback);
  313. }
  314. }
  315. });