Created
April 15, 2015 19:43
-
-
Save krosti/eb545870b78cae19a14f to your computer and use it in GitHub Desktop.
Angular-oneway directive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
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; | |
}; | |
var isIE = function() { | |
var ie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); | |
if (isNaN(ie)) { | |
ie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); | |
} | |
return ie; | |
} | |
var showHideBinder = function(elm, attr, value) { | |
var show = (attr === 'show') ? '' : 'none'; | |
var hide = (attr === 'hide') ? '' : 'none'; | |
elm.css('display', toBo olean(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 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); | |
}); | |
}); | |
}; | |
///-------Directive Methods | |
angular | |
.module('tcApp') | |
.directive('oneway', [function() { | |
return { | |
restrict: "AM", | |
controller: ['$scope', '$element', '$attrs', '$interpolate', function($scope, $element, $attrs, $interpolate) { | |
return { | |
watcherRemover: undefined, | |
binders: [], | |
group: $attrs.oneName, | |
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 oneway, if the bo-children | |
// are not dom children the linking can follow another order | |
if (this.ran) { | |
this.runBinders(); | |
} | |
}, | |
setupWatcher: function(onewayValue) { | |
var that = this; | |
this.watcherRemover = $scope.$watch(onewayValue, function(newValue) { | |
if (newValue === undefined) return; | |
that.removeWatcher(); | |
that.checkBindonce(newValue); | |
}, true); | |
}, | |
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() { | |
if (this.watcherRemover !== undefined) { | |
this.watcherRemover(); | |
this.watcherRemover = undefined; | |
} | |
}, | |
runBinders: function() { | |
while (this.binders.length > 0) { | |
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) { | |
case 'oneIf': | |
if (toBoolean(value)) { | |
transclude(binder, binder.scope.$new()); | |
} | |
break; | |
case 'oneSwitch': | |
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 'oneSwitchWhen': | |
var ctrl = binder.controller[0]; | |
ctrl.cases['!' + binder.attrs.oneSwitchWhen] = (ctrl.cases['!' + binder.attrs.oneSwitchWhen] || []); | |
ctrl.cases['!' + binder.attrs.oneSwitchWhen].push({ | |
transclude: binder.transclude, | |
element: binder.element | |
}); | |
break; | |
case 'oneSwitchDefault': | |
var ctrl = binder.controller[0]; | |
ctrl.cases['?'] = (ctrl.cases['?'] || []); | |
ctrl.cases['?'].push({ | |
transclude: binder.transclude, | |
element: binder.element | |
}); | |
break; | |
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 'style': | |
binder.element.css(value); | |
break; | |
case 'disabled': | |
binder.element.prop('disabled', value); | |
break; | |
case 'src': | |
binder.element.attr(binder.attr, value); | |
if (isIE()) binder.element.prop('src', value); | |
break; | |
case 'attr': | |
angular.forEach(binder.attrs, function(attrValue, attrKey) { | |
var newAttr, newValue; | |
if (attrKey.match(/^oneAttr./) && binder.attrs[attrKey]) { | |
newAttr = attrKey.replace(/^oneAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
newValue = binder.scope.$eval(binder.attrs[attrKey]); | |
binder.element.attr(newAttr, newValue); | |
} | |
}); | |
break; | |
case 'href': | |
case 'alt': | |
case 'title': | |
case 'id': | |
case 'value': | |
binder.element.attr(binder.attr, value); | |
break; | |
} | |
} | |
this.ran = true; | |
} | |
}; | |
}], | |
link: function(scope, elm, attrs, onewayController) { | |
var value = attrs.oneway && scope.$eval(attrs.oneway); | |
if (value !== undefined) { | |
onewayController.checkBindonce(value); | |
} else { | |
onewayController.setupWatcher(attrs.oneway); | |
elm.bind("$destroy", onewayController.removeWatcher); | |
} | |
} | |
}; | |
}); | |
angular.forEach( | |
[{ | |
directiveName: 'oneShow', | |
attribute: 'show' | |
}, { | |
directiveName: 'oneHide', | |
attribute: 'hide' | |
}, { | |
directiveName: 'oneClass', | |
attribute: 'class' | |
}, { | |
directiveName: 'oneText', | |
attribute: 'text' | |
}, { | |
directiveName: 'oneBind', | |
attribute: 'text' | |
}, { | |
directiveName: 'oneHtml', | |
attribute: 'html' | |
}, { | |
directiveName: 'oneSrcI', | |
attribute: 'src', | |
interpolate: true | |
}, { | |
directiveName: 'oneSrc', | |
attribute: 'src' | |
}, { | |
directiveName: 'oneHrefI', | |
attribute: 'href', | |
interpolate: true | |
}, { | |
directiveName: 'oneHref', | |
attribute: 'href' | |
}, { | |
directiveName: 'oneAlt', | |
attribute: 'alt' | |
}, { | |
directiveName: 'oneTitle', | |
attribute: 'title' | |
}, { | |
directiveName: 'oneId', | |
attribute: 'id' | |
}, { | |
directiveName: 'oneStyle', | |
attribute: 'style' | |
}, { | |
directiveName: 'oneDisabled', | |
attribute: 'disabled' | |
}, { | |
directiveName: 'oneValue', | |
attribute: 'value' | |
}, { | |
directiveName: 'oneAttr', | |
attribute: 'attr' | |
}, | |
{ | |
directiveName: 'oneIf', | |
transclude: 'element', | |
terminal: true, | |
priority: 1000 | |
}, { | |
directiveName: 'oneSwitch', | |
require: 'oneSwitch', | |
controller: function() { | |
this.cases = {}; | |
} | |
}, { | |
directiveName: 'oneSwitchWhen', | |
transclude: 'element', | |
priority: 800, | |
require: '^oneSwitch' | |
}, { | |
directiveName: 'oneSwitchDefault', | |
transclude: 'element', | |
priority: 800, | |
require: '^oneSwitch' | |
} | |
], | |
function(oneDirective) { | |
return onewayModule.directive(oneDirective.directiveName, function() { | |
var onewayDirective = { | |
priority: oneDirective.priority || 200, | |
transclude: oneDirective.transclude || false, | |
terminal: oneDirective.terminal || false, | |
require: ['^oneway'].concat(oneDirective.require || []), | |
controller: oneDirective.controller, | |
compile: function(tElement, tAttrs, transclude) { | |
return function(scope, elm, attrs, controllers) { | |
var onewayController = controllers[0], | |
name = attrs.oneParent, | |
element = onewayController.element.parent(), | |
parentValue; | |
if (name && onewayController.group !== name) { | |
onewayController = undefined; | |
while (element[0].nodeType !== 9 && element.length) { | |
if ((parentValue = element.data('$onewayController')) && parentValue.group === name) { | |
onewayController = parentValue; | |
break; | |
} | |
element = element.parent(); | |
} | |
if (!onewayController) | |
throw new Error("Missing controller oneway: " + name); | |
} | |
onewayController.addBinder({ | |
element : elm, | |
attr : oneDirective.attribute || oneDirective.directiveName, | |
attrs : attrs, | |
value : attrs[oneDirective.directiveName], | |
interpolate : oneDirective.interpolate, | |
group : name, | |
transclude : transclude, | |
controller : controllers.slice(1), | |
scope : scope | |
}); | |
}; | |
} | |
}; | |
return onewayDirective; | |
}); | |
}) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment