'use strict'; /** * Bindonce - Zero watches binding for AngularJs * @version v0.1.1 - 2013-05-07 * @link https://github.com/Pasvaz/bindonce * @author Pasquale Vazzana * @license MIT License, http://www.opensource.org/licenses/MIT */ angular.module('pasvaz.bindonce', []) .directive('bindonce', function() { var toBoolean = function(value) { if (value && value.length !== 0) { var v = angular.lowercase("" + value); value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); } else { value = false; } return value; } return { restrict: "AM", controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) { var showHideBinder = function(elm, attr, value) { var show = (attr == 'show') ? '' : 'none'; var hide = (attr == 'hide') ? '' : 'none'; elm.css('display', toBoolean(value) ? show : hide); } var classBinder = function(elm, value) { if (angular.isObject(value) && !angular.isArray(value)) { var results = []; angular.forEach(value, function(value, index) { if (value) results.push(index); }); value = results; } if (value) { elm.addClass(angular.isArray(value) ? value.join(' ') : value); } } var ctrl = { watcherRemover : undefined, binders : [], group : $attrs.boName, element : $element, ran : false, addBinder : function(binder) { this.binders.push(binder); // In case of late binding (when using the directive bo-name/bo-parent) // it happens only when you use nested bindonce, if the bo-children // are not dom children the linking can follow another order if (this.ran) { this.runBinders(); } }, setupWatcher : function(bindonceValue) { var that = this; this.watcherRemover = $scope.$watch(bindonceValue, function(newValue) { if (newValue == undefined) return; that.removeWatcher(); that.runBinders(); }, true); }, removeWatcher : function() { if (this.watcherRemover != undefined) { this.watcherRemover(); this.watcherRemover = undefined; } }, runBinders : function() { for (var data in this.binders) { var binder = this.binders[data]; if (this.group && this.group != binder.group ) continue; var value = $scope.$eval(binder.value); switch(binder.attr) { case 'hide': case 'show': showHideBinder(binder.element, binder.attr, value); break; case 'class': classBinder(binder.element, value); break; case 'text': binder.element.text(value); break; case 'html': binder.element.html(value); break; case 'src': case 'href': case 'alt': case 'title': case 'id': case 'style': case 'value': binder.element.attr(binder.attr, value); break; } } this.ran = true; this.binders = []; } } return ctrl; }], link: function(scope, elm, attrs, bindonceController) { var value = (attrs.bindonce) ? scope.$eval(attrs.bindonce) : true; if (value != undefined) { bindonceController.runBinders(); } else { bindonceController.setupWatcher(attrs.bindonce); elm.bind("$destroy", bindonceController.removeWatcher); } } }; }); angular.forEach({ 'boShow' : 'show', 'boHide' : 'hide', 'boClass' : 'class', 'boText' : 'text', 'boHtml' : 'html', 'boSrc' : 'src', 'boHref' : 'href', 'boAlt' : 'alt', 'boTitle' : 'title', 'boId' : 'id', 'boStyle' : 'style', 'boValue' : 'value' }, function(tag, attribute) { var childPriority = 200; return angular.module('pasvaz.bindonce').directive(attribute, function() { return { priority: childPriority, require: '^bindonce', link: function(scope, elm, attrs, bindonceController) { var name = attrs.boParent; if (name && bindonceController.group != name) { var element = bindonceController.element.parent(); bindonceController = undefined; var parentValue; while (element[0].nodeType != 9 && element.length) { if ((parentValue = element.data('$bindonceController')) && parentValue.group == name) { bindonceController = parentValue break; } element = element.parent(); } if (!bindonceController) { throw Error("No bindonce controller: " + name); } } bindonceController.addBinder({element: elm, attr:tag, value: attrs[attribute], group: name}); } } }); });