Pony.fm/resources/assets/scripts/base/bindonce.js

326 lines
9.6 KiB
JavaScript
Raw Normal View History

2016-02-14 05:29:13 +01:00
(function () {
"use strict";
/**
* Bindonce - Zero watches binding for AngularJs
* @version v0.3.3
* @link https://github.com/Pasvaz/bindonce
* @author Pasquale Vazzana <pasqualevazzana@gmail.com>
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
var bindonceModule = angular.module('pasvaz.bindonce', []);
bindonceModule.directive('bindonce', function ()
{
var toBoolean = function (value)
{
if (value && value.length !== 0)
{
2015-08-31 17:42:21 +02:00
var v = angular.lowercase("" + value);
2016-02-14 05:29:13 +01:00
value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
}
else
{
2015-08-31 17:42:21 +02:00
value = false;
}
return value;
2016-02-14 05:29:13 +01:00
};
var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
if (isNaN(msie))
{
msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
2015-08-31 17:42:21 +02:00
}
2016-02-14 05:29:13 +01:00
var bindonceDirective =
{
2015-08-31 17:42:21 +02:00
restrict: "AM",
2016-02-14 05:29:13 +01:00
controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
{
var showHideBinder = function (elm, attr, value)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
var show = (attr === 'show') ? '' : 'none';
var hide = (attr === 'hide') ? '' : 'none';
2015-08-31 17:42:21 +02:00
elm.css('display', toBoolean(value) ? show : hide);
2016-02-14 05:29:13 +01:00
};
var classBinder = function (elm, value)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
if (angular.isObject(value) && !angular.isArray(value))
{
2015-08-31 17:42:21 +02:00
var results = [];
2016-02-14 05:29:13 +01:00
angular.forEach(value, function (value, index)
{
2015-08-31 17:42:21 +02:00
if (value) results.push(index);
});
value = results;
}
2016-02-14 05:29:13 +01:00
if (value)
{
2015-08-31 17:42:21 +02:00
elm.addClass(angular.isArray(value) ? value.join(' ') : value);
}
2016-02-14 05:29:13 +01:00
};
var transclude = function (transcluder, scope)
{
transcluder.transclude(scope, function (clone)
{
var parent = transcluder.element.parent();
var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
angular.forEach(clone, function (node)
{
parentNode.insertBefore(node, afterNextSibling);
});
});
};
2015-08-31 17:42:21 +02:00
var ctrl =
{
2016-02-14 05:29:13 +01:00
watcherRemover: undefined,
binders: [],
group: $attrs.boName,
element: $element,
ran: false,
2015-08-31 17:42:21 +02:00
2016-02-14 05:29:13 +01:00
addBinder: function (binder)
2015-08-31 17:42:21 +02:00
{
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();
}
},
2016-02-14 05:29:13 +01:00
setupWatcher: function (bindonceValue)
2015-08-31 17:42:21 +02:00
{
var that = this;
2016-02-14 05:29:13 +01:00
this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
if (newValue === undefined) return;
2015-08-31 17:42:21 +02:00
that.removeWatcher();
2016-02-14 05:29:13 +01:00
that.checkBindonce(newValue);
2015-08-31 17:42:21 +02:00
}, true);
},
2016-02-14 05:29:13 +01:00
checkBindonce: function (value)
{
var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
// since Angular 1.2 promises are no longer
// undefined until they don't get resolved
if (typeof promise === 'function')
{
promise(function ()
{
that.runBinders();
});
}
else
{
that.runBinders();
}
},
removeWatcher: function ()
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
if (this.watcherRemover !== undefined)
2015-08-31 17:42:21 +02:00
{
this.watcherRemover();
this.watcherRemover = undefined;
}
},
2016-02-14 05:29:13 +01:00
runBinders: function ()
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
while (this.binders.length > 0)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
var binder = this.binders.shift();
if (this.group && this.group != binder.group) continue;
var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
switch (binder.attr)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
case 'boIf':
if (toBoolean(value))
{
transclude(binder, binder.scope.$new());
}
break;
case 'boSwitch':
var selectedTranscludes, switchCtrl = binder.controller[0];
if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
{
binder.scope.$eval(binder.attrs.change);
angular.forEach(selectedTranscludes, function (selectedTransclude)
{
transclude(selectedTransclude, binder.scope.$new());
});
}
break;
case 'boSwitchWhen':
var ctrl = binder.controller[0];
ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
break;
case 'boSwitchDefault':
var ctrl = binder.controller[0];
ctrl.cases['?'] = (ctrl.cases['?'] || []);
ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
break;
2015-08-31 17:42:21 +02:00
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;
2016-02-14 05:29:13 +01:00
case 'style':
binder.element.css(value);
break;
case 'disabled':
binder.element.prop('disabled', value);
break;
2015-08-31 17:42:21 +02:00
case 'src':
2016-02-14 05:29:13 +01:00
binder.element.attr(binder.attr, value);
if (msie) binder.element.prop('src', value);
break;
case 'attr':
angular.forEach(binder.attrs, function (attrValue, attrKey)
{
var newAttr, newValue;
if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
{
newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
newValue = binder.scope.$eval(binder.attrs[attrKey]);
binder.element.attr(newAttr, newValue);
}
});
break;
2015-08-31 17:42:21 +02:00
case 'href':
case 'alt':
case 'title':
case 'id':
case 'value':
binder.element.attr(binder.attr, value);
break;
}
}
this.ran = true;
}
2016-02-14 05:29:13 +01:00
};
2015-08-31 17:42:21 +02:00
2016-02-14 05:29:13 +01:00
angular.extend(this, ctrl);
2015-08-31 17:42:21 +02:00
}],
2016-02-14 05:29:13 +01:00
link: function (scope, elm, attrs, bindonceController)
{
var value = attrs.bindonce && scope.$eval(attrs.bindonce);
if (value !== undefined)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
bindonceController.checkBindonce(value);
2015-08-31 17:42:21 +02:00
}
else
{
bindonceController.setupWatcher(attrs.bindonce);
elm.bind("$destroy", bindonceController.removeWatcher);
}
}
};
2016-02-14 05:29:13 +01:00
return bindonceDirective;
2015-08-31 17:42:21 +02:00
});
2016-02-14 05:29:13 +01:00
angular.forEach(
[
{ directiveName: 'boShow', attribute: 'show' },
{ directiveName: 'boHide', attribute: 'hide' },
{ directiveName: 'boClass', attribute: 'class' },
{ directiveName: 'boText', attribute: 'text' },
{ directiveName: 'boBind', attribute: 'text' },
{ directiveName: 'boHtml', attribute: 'html' },
{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
{ directiveName: 'boSrc', attribute: 'src' },
{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
{ directiveName: 'boHref', attribute: 'href' },
{ directiveName: 'boAlt', attribute: 'alt' },
{ directiveName: 'boTitle', attribute: 'title' },
{ directiveName: 'boId', attribute: 'id' },
{ directiveName: 'boStyle', attribute: 'style' },
{ directiveName: 'boDisabled', attribute: 'disabled' },
{ directiveName: 'boValue', attribute: 'value' },
{ directiveName: 'boAttr', attribute: 'attr' },
{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
],
function (boDirective)
2015-08-31 17:42:21 +02:00
{
var childPriority = 200;
2016-02-14 05:29:13 +01:00
return bindonceModule.directive(boDirective.directiveName, function ()
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
var bindonceDirective =
{
priority: boDirective.priority || childPriority,
transclude: boDirective.transclude || false,
terminal: boDirective.terminal || false,
require: ['^bindonce'].concat(boDirective.require || []),
controller: boDirective.controller,
compile: function (tElement, tAttrs, transclude)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
return function (scope, elm, attrs, controllers)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
var bindonceController = controllers[0];
var name = attrs.boParent;
if (name && bindonceController.group !== name)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
var element = bindonceController.element.parent();
bindonceController = undefined;
var parentValue;
while (element[0].nodeType !== 9 && element.length)
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
if ((parentValue = element.data('$bindonceController'))
&& parentValue.group === name)
{
bindonceController = parentValue;
break;
}
element = element.parent();
}
if (!bindonceController)
{
throw new Error("No bindonce controller: " + name);
2015-08-31 17:42:21 +02:00
}
}
2016-02-14 05:29:13 +01:00
bindonceController.addBinder(
2015-08-31 17:42:21 +02:00
{
2016-02-14 05:29:13 +01:00
element: elm,
attr: boDirective.attribute || boDirective.directiveName,
attrs: attrs,
value: attrs[boDirective.directiveName],
interpolate: boDirective.interpolate,
group: name,
transclude: transclude,
controller: controllers.slice(1),
scope: scope
});
};
2015-08-31 17:42:21 +02:00
}
2016-02-14 05:29:13 +01:00
};
return bindonceDirective;
2015-08-31 17:42:21 +02:00
});
2016-02-14 05:29:13 +01:00
})
})();