-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #368 from vitaminac/master
add folder structure to select files in select-file option
- Loading branch information
Showing
5 changed files
with
199 additions
and
14 deletions.
There are no files selected for viewing
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// watches changes in the file upload control (input[file]) and | ||
// puts the files selected in an attribute | ||
var app = angular.module("webui.directives.fileselect", ["webui.services.deps"]); | ||
app.directive("indeterminate", [ | ||
"$parse", function syncInput (parse) { | ||
return { | ||
require : "ngModel", | ||
// Restrict the directive so it can only be used as an attribute | ||
restrict : "A", | ||
|
||
link : function link (scope, elem, attr, ngModelCtrl) { | ||
// Whenever the bound value of the attribute changes we update | ||
// use upward emit notification for change to prevent the performance penalty bring by $scope.$watch | ||
var getter = parse(attr["ngModel"]); | ||
// var setter = getter.assign; | ||
var children = []; // cache children input | ||
var cacheSelectedSubInputNumber = 0; | ||
var cacheNoSelectedSubInputNumber = 0; | ||
var get = function () { | ||
return getter(scope); | ||
}; | ||
// the internal 'indeterminate' flag on the attached dom element | ||
var setIndeterminateState = function (newValue) { | ||
elem.prop("indeterminate", newValue); | ||
}; | ||
var setModelValueWithSideEffect = function (newVal) { // will cause to emit corresponding events | ||
ngModelCtrl.$setViewValue(newVal); | ||
ngModelCtrl.$render(); | ||
}; | ||
var passIfIsLeafChild = function (callback) { // ensure to execute callback only when this input has one or more subinputs | ||
return function () { | ||
if (children.length > 0) { | ||
callback.apply(this, arguments); | ||
} | ||
}; | ||
}; | ||
var passIfNotIsLeafChild = function (callback) { // ensure to execute callback only when this input hasn't any subinput | ||
return function () { | ||
if (children.length === 0) { | ||
callback.apply(this, arguments); | ||
} | ||
}; | ||
}; | ||
var passThroughThisScope = function (callback) { // pass through the event from the scope where they sent | ||
return function (event) { | ||
if (event.targetScope !== event.currentScope) { | ||
return callback.apply(this, arguments); | ||
} | ||
}; | ||
}; | ||
var passIfIsIndeterminate = function (callback) { // pass through the event when this input is indeterminate | ||
return function () { | ||
if (!elem.prop("indeterminate")) { | ||
return callback.apply(this, arguments); | ||
} | ||
}; | ||
}; | ||
/* var catchEventOnlyOnce = function (callback) { // only fire once, and stop event's propagation | ||
return function (event) { | ||
callback.apply(this, arguments); | ||
return event.stopPropagation(); | ||
}; | ||
}; */ | ||
if (attr["indeterminate"] && parse(attr["indeterminate"]).constant) { | ||
setIndeterminateState(scope.$eval(attr["indeterminate"])); // set to default value (set in template) | ||
} | ||
if (attr["indeterminate"] && parse(attr["indeterminate"]).constant && !scope.$eval(attr["indeterminate"])) { | ||
// when this input wont have subinput, they will only receive parent change and emit child change event | ||
ngModelCtrl.$viewChangeListeners.push(passIfNotIsLeafChild(function () { | ||
scope.$emit("childSelectedChange", get()); // notifies parents to change their state | ||
})); | ||
scope.$on("ParentSelectedChange", passThroughThisScope(passIfNotIsLeafChild( | ||
function (event, newVal) { | ||
setModelValueWithSideEffect(newVal); // set value to parent's value; this will cause listener to emit childChange event; this won't be a infinite loop | ||
}))); | ||
// init first time and only once | ||
scope.$emit("i'm child input", get); // traverses upwards toward the root scope notifying the listeners for keep reference to this input's value | ||
scope.$emit("childSelectedChange", get()); // force emitted, and force the parent change their state base on children at first time | ||
} else { | ||
// establish parent-child's relation | ||
// listen for the child emitted token | ||
scope.$on("i'm child input", passThroughThisScope( // can't apply pass passIfIsLeafChild, at beginning all has not child input | ||
function (event, child) { | ||
children.push(child); | ||
}) | ||
); | ||
var updateBaseOnChildrenState = function (event, newChildValue) { | ||
if ((cacheSelectedSubInputNumber + cacheNoSelectedSubInputNumber) !== children.length) { | ||
// cache childern state | ||
cacheSelectedSubInputNumber = 0; | ||
cacheNoSelectedSubInputNumber = 0; | ||
for (var i = 0; i < children.length; i++) { | ||
if (children[i]()) { | ||
cacheSelectedSubInputNumber += 1; | ||
} else { | ||
cacheNoSelectedSubInputNumber += 1; | ||
} | ||
} | ||
} else { | ||
// no need for recalculated children state | ||
// just make a few change to cache value | ||
if (newChildValue) { | ||
cacheSelectedSubInputNumber++; | ||
cacheNoSelectedSubInputNumber--; | ||
} else { | ||
cacheSelectedSubInputNumber--; | ||
cacheNoSelectedSubInputNumber++; | ||
} | ||
} | ||
var allSelected = (cacheNoSelectedSubInputNumber === 0); // if doesn't has any no selected input | ||
var anySeleted = (cacheSelectedSubInputNumber > 0); | ||
setIndeterminateState(allSelected !== anySeleted); // if at least one is selected, but not all then set input property indeterminate to true | ||
setModelValueWithSideEffect(allSelected); // change binding model value and trigger onchange event | ||
}; | ||
// is not leaf input, Only receive child change and parent change event | ||
ngModelCtrl.$viewChangeListeners.push(passIfIsLeafChild(passIfIsIndeterminate(function () { | ||
// emit when input property indeterminate is false, prevent recursively emitting event from parent to children, children to parent | ||
scope.$broadcast("ParentSelectedChange", get()); | ||
}))); | ||
// reset input state base on children inputs | ||
scope.$on("childSelectedChange", passThroughThisScope(passIfIsLeafChild(updateBaseOnChildrenState))); | ||
} | ||
} | ||
}; | ||
} | ||
]); |
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