bindonce.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. 'use strict';
  2. /**
  3. * Bindonce - Zero watches binding for AngularJs
  4. * @version v0.2.1 - 2013-05-07
  5. * @link https://github.com/Pasvaz/bindonce
  6. * @author Pasquale Vazzana <pasqualevazzana@gmail.com>
  7. * @license MIT License, http://www.opensource.org/licenses/MIT
  8. */
  9. angular.module('pasvaz.bindonce', [])
  10. .directive('bindonce', function()
  11. {
  12. var toBoolean = function(value)
  13. {
  14. if (value && value.length !== 0)
  15. {
  16. var v = angular.lowercase("" + value);
  17. value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
  18. }
  19. else
  20. {
  21. value = false;
  22. }
  23. return value;
  24. }
  25. var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
  26. if (isNaN(msie))
  27. {
  28. msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
  29. }
  30. var bindonceDirective =
  31. {
  32. restrict: "AM",
  33. controller: ['$scope', '$element', '$attrs', '$interpolate', function($scope, $element, $attrs, $interpolate)
  34. {
  35. var showHideBinder = function(elm, attr, value)
  36. {
  37. var show = (attr == 'show') ? '' : 'none';
  38. var hide = (attr == 'hide') ? '' : 'none';
  39. elm.css('display', toBoolean(value) ? show : hide);
  40. }
  41. var classBinder = function(elm, value)
  42. {
  43. if (angular.isObject(value) && !angular.isArray(value))
  44. {
  45. var results = [];
  46. angular.forEach(value, function(value, index)
  47. {
  48. if (value) results.push(index);
  49. });
  50. value = results;
  51. }
  52. if (value)
  53. {
  54. elm.addClass(angular.isArray(value) ? value.join(' ') : value);
  55. }
  56. }
  57. var ctrl =
  58. {
  59. watcherRemover : undefined,
  60. binders : [],
  61. group : $attrs.boName,
  62. element : $element,
  63. ran : false,
  64. addBinder : function(binder)
  65. {
  66. this.binders.push(binder);
  67. // In case of late binding (when using the directive bo-name/bo-parent)
  68. // it happens only when you use nested bindonce, if the bo-children
  69. // are not dom children the linking can follow another order
  70. if (this.ran)
  71. {
  72. this.runBinders();
  73. }
  74. },
  75. setupWatcher : function(bindonceValue)
  76. {
  77. var that = this;
  78. this.watcherRemover = $scope.$watch(bindonceValue, function(newValue)
  79. {
  80. if (newValue == undefined) return;
  81. that.removeWatcher();
  82. that.runBinders();
  83. }, true);
  84. },
  85. removeWatcher : function()
  86. {
  87. if (this.watcherRemover != undefined)
  88. {
  89. this.watcherRemover();
  90. this.watcherRemover = undefined;
  91. }
  92. },
  93. runBinders : function()
  94. {
  95. var i, max;
  96. for (i = 0, max = this.binders.length; i < max; i ++)
  97. {
  98. var binder = this.binders[i];
  99. if (this.group && this.group != binder.group ) continue;
  100. var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
  101. switch(binder.attr)
  102. {
  103. case 'if':
  104. if (toBoolean(value))
  105. {
  106. binder.transclude(binder.scope.$new(), function (clone)
  107. {
  108. var parent = binder.element.parent();
  109. var afterNode = binder.element && binder.element[binder.element.length - 1];
  110. var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
  111. var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
  112. angular.forEach(clone, function(node)
  113. {
  114. parentNode.insertBefore(node, afterNextSibling);
  115. });
  116. });
  117. }
  118. break;
  119. case 'hide':
  120. case 'show':
  121. showHideBinder(binder.element, binder.attr, value);
  122. break;
  123. case 'class':
  124. classBinder(binder.element, value);
  125. break;
  126. case 'text':
  127. binder.element.text(value);
  128. break;
  129. case 'html':
  130. binder.element.html(value);
  131. break;
  132. case 'style':
  133. binder.element.css(value);
  134. break;
  135. case 'src':
  136. binder.element.attr(binder.attr, value);
  137. if (msie) binder.element.prop('src', value);
  138. case 'attr':
  139. angular.forEach(binder.attrs, function(attrValue, attrKey)
  140. {
  141. var newAttr, newValue;
  142. if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
  143. {
  144. newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  145. newValue = binder.scope.$eval(binder.attrs[attrKey]);
  146. binder.element.attr(newAttr, newValue);
  147. }
  148. });
  149. break;
  150. case 'href':
  151. case 'alt':
  152. case 'title':
  153. case 'id':
  154. case 'value':
  155. binder.element.attr(binder.attr, value);
  156. break;
  157. }
  158. }
  159. this.ran = true;
  160. this.binders = [];
  161. }
  162. }
  163. return ctrl;
  164. }],
  165. link: function(scope, elm, attrs, bindonceController)
  166. {
  167. var value = (attrs.bindonce) ? scope.$eval(attrs.bindonce) : true;
  168. if (value != undefined)
  169. {
  170. bindonceController.runBinders();
  171. }
  172. else
  173. {
  174. bindonceController.setupWatcher(attrs.bindonce);
  175. elm.bind("$destroy", bindonceController.removeWatcher);
  176. }
  177. }
  178. };
  179. return bindonceDirective;
  180. });
  181. angular.forEach(
  182. [
  183. {directiveName:'boShow', attribute: 'show'},
  184. {directiveName:'boIf', attribute: 'if', transclude: 'element', terminal: true, priority:1000},
  185. {directiveName:'boHide', attribute:'hide'},
  186. {directiveName:'boClass', attribute:'class'},
  187. {directiveName:'boText', attribute:'text'},
  188. {directiveName:'boHtml', attribute:'html'},
  189. {directiveName:'boSrcI', attribute:'src', interpolate:true},
  190. {directiveName:'boSrc', attribute:'src'},
  191. {directiveName:'boHrefI', attribute:'href', interpolate:true},
  192. {directiveName:'boHref', attribute:'href'},
  193. {directiveName:'boAlt', attribute:'alt'},
  194. {directiveName:'boTitle', attribute:'title'},
  195. {directiveName:'boId', attribute:'id'},
  196. {directiveName:'boStyle', attribute:'style'},
  197. {directiveName:'boValue', attribute:'value'},
  198. {directiveName:'boAttr', attribute:'attr'}
  199. ],
  200. function(boDirective)
  201. {
  202. var childPriority = 200;
  203. return angular.module('pasvaz.bindonce').directive(boDirective.directiveName, function()
  204. {
  205. var bindonceDirective =
  206. {
  207. priority: boDirective.priority || childPriority,
  208. transclude: boDirective.transclude || false,
  209. terminal: boDirective.terminal || false,
  210. require: '^bindonce',
  211. compile: function (tElement, tAttrs, transclude)
  212. {
  213. return function(scope, elm, attrs, bindonceController)
  214. {
  215. var name = attrs.boParent;
  216. if (name && bindonceController.group != name)
  217. {
  218. var element = bindonceController.element.parent();
  219. bindonceController = undefined;
  220. var parentValue;
  221. while (element[0].nodeType != 9 && element.length)
  222. {
  223. if ((parentValue = element.data('$bindonceController'))
  224. && parentValue.group == name)
  225. {
  226. bindonceController = parentValue
  227. break;
  228. }
  229. element = element.parent();
  230. }
  231. if (!bindonceController)
  232. {
  233. throw Error("No bindonce controller: " + name);
  234. }
  235. }
  236. bindonceController.addBinder(
  237. {
  238. element : elm,
  239. attr : boDirective.attribute,
  240. attrs : attrs,
  241. value : attrs[boDirective.directiveName],
  242. interpolate : boDirective.interpolate,
  243. group : name,
  244. transclude : transclude,
  245. scope : scope
  246. });
  247. }
  248. }
  249. }
  250. return bindonceDirective;
  251. });
  252. });