diff --git a/index.html b/index.html index 6358a6f..c58e77e 100644 --- a/index.html +++ b/index.html @@ -1040,7 +1040,7 @@
- {{folderName}} + {{folderName}} {{ 'click the text to expand the folder' | translate }} {{ 'click the text to collapse the folder' | translate }}
@@ -1055,7 +1055,7 @@

diff --git a/js/ctrls/modal.js b/js/ctrls/modal.js index b203de9..4fe4446 100644 --- a/js/ctrls/modal.js +++ b/js/ctrls/modal.js @@ -118,47 +118,8 @@ angular this.dirs = {}; this.files = []; this.show = false; - this._selected = false; + this.selected = true; } - OrganizedFolder.prototype.change = function () { - for (var i = 0; i < this.files.length; i++) { - this.files[i].selected = this.selected; - } - for (var folder in this.dirs) { - if (this.dirs.hasOwnProperty(folder)) { - this.dirs[folder].selected = this.selected; - } - } - }; - Object.defineProperty(OrganizedFolder.prototype, "selected", { - get : function () { - return this._selected; - }, - set : function (newValue) { - this._selected = newValue; - this.change(); - } - }); - Object.defineProperty(OrganizedFolder.prototype, "indeterminate", { - get : function () { - var allSeleted = true; - var allNotSelected = true; - for (var p in this.dirs) { - if (this.dirs.hasOwnProperty(p)) { - if (this.dirs[p].indeterminate) { - return true; - } - allSeleted &= this.dirs[p].selected; - allNotSelected &= !this.dirs[p].selected; - } - } - for (var i = 0; i < this.files.length; i++) { - allSeleted &= this.files[i].selected; - allNotSelected &= !this.files[i].selected; - } - return !allSeleted && !allNotSelected;// is not determinate when either all selected either all not selected; - } - }); var folder = new OrganizedFolder(), tmp; for (var i = 0; i < files.length; i++) { tmp = folder; @@ -172,7 +133,6 @@ angular } tmp.files.push(files[i]); } - folder.change(); return folder; }; this.groupedFiles = groupFiles(this.files); diff --git a/js/directives/fileselect.js b/js/directives/fileselect.js index b3efbd5..f2b1cdc 100644 --- a/js/directives/fileselect.js +++ b/js/directives/fileselect.js @@ -1,57 +1,98 @@ // watches changes in the file upload control (input[file]) and // puts the files selected in an attribute -var app = angular.module("webui.directives.fileselect", []); -app.directive("indeterminate", function () { - return { - // Restrict the directive so it can only be used as an attribute - restrict : "A", +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) { - // Whenever the bound value of the attribute changes we update - // the internal 'indeterminate' flag on the attached dom element - // use upward emit notification for change to prevent the performance penalty bring by $scope.$watch - elem[0].indeterminate = scope.$eval(attr.indeterminate); - scope.$on("childSelectedChange", function (event, options) { - elem[0].indeterminate = scope.$eval(attr.indeterminate); - }); - - /* var watcher = scope.$watch(attr.indeterminate, function (value) { - elem[0].indeterminate = value; - }); - - // Remove the watcher when the directive is destroyed - scope.$on("$destroy", function () { - watcher(); - }); */ - } - }; -}); - -app.directive("monitor", function () { - return { - // Restrict the directive so it can only be used as an attribute - restrict : "A", - - link : function link (scope, elem, attr) { - // Whenever the bound value of the attribute changes we update - // the internal 'indeterminate' flag on the attached dom element - elem.change(function () { - scope.$emit("childSelectedChange"); - }); - } - }; -}); - -app.directive("selected", function () { - return { - // Restrict the directive so it can only be used as an attribute - restrict : "A", - - link : function link (scope, elem, attr) { - elem[0].value = scope.$eval(attr.selected); - scope.$on("childSelectedChange", function (event, options) { - elem[0].value = scope.$eval(attr.selected); - }); - } - }; -}); + 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 get = function () { + return getter(scope); + }; + // the internal 'indeterminate' flag on the attached dom element + var setIndeterminateState = function (newValue) { + elem.prop("indeterminate", newValue); + }; + var setModelValueWithoutSideEffect = function (newVal) { + setter(scope, newVal); + ngModelCtrl.$modelValue = newVal; + ngModelCtrl.$viewValue = newVal; + ngModelCtrl.$render(); + }; + var setWithSideEffect = function (newVal) { + ngModelCtrl.$setViewValue(newVal); + ngModelCtrl.$render(); + }; + var passIfLeafChild = function (callback) { + return function () { + if (children.length > 0) { + callback.apply(this, arguments); + } + }; + }; + var passIfNotIsLeafChild = function (callback) { + return function () { + if (children.length === 0) { + callback.apply(this, arguments); + } + }; + }; + var passThroughThisScope = function (callback) { + return function (event) { + if (event.targetScope !== event.currentScope) { + return callback.apply(this, arguments); + } + }; + }; + if (attr["indeterminate"] && parse(attr["indeterminate"]).constant && !scope.$eval(attr["indeterminate"])) { + // is leaf input, Only receive parent change and emit child change event + setIndeterminateState(scope.$eval(attr["indeterminate"])); + ngModelCtrl.$viewChangeListeners.push(passIfNotIsLeafChild(function () { + scope.$emit("childSelectedChange"); + })); + scope.$on("ParentSelectedChange", passThroughThisScope( + function (event, newVal) { + setWithSideEffect(newVal); + })); + scope.$emit("i'm child input", get); + setWithSideEffect(get()); + scope.$emit("childSelectedChange"); + } else { + // init first time and only once + // establish parent-child's relation + scope.$on("i'm child input", passThroughThisScope( + function (event, child) { + children.push(child); + }) + ); + var updateBaseOnChildrenState = function () { + var allSelected = children.every(function (child) { + return child(); + }); + var anySeleted = children.some(function (child) { + return child(); + }); + setIndeterminateState(allSelected !== anySeleted); + setWithSideEffect(allSelected); + }; + // is not leaf input, Only receive child change and parent change event + ngModelCtrl.$viewChangeListeners.push(passIfLeafChild(function () { + if (!elem.prop("indeterminate")) { // emit when prop indeterminate is set to false + scope.$broadcast("ParentSelectedChange", get()); + } + })); + // reset input state base on children inputs + scope.$on("childSelectedChange", passThroughThisScope(passIfLeafChild(updateBaseOnChildrenState))); + } + } + }; + } +]);