Skip to content

Commit

Permalink
Merge branch 'master' into FLUID-5200
Browse files Browse the repository at this point in the history
  • Loading branch information
jobara committed Nov 4, 2013
2 parents 328c1db + 1a34a90 commit 74e0503
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 85 deletions.
26 changes: 14 additions & 12 deletions src/demos/keyboard-a11y/js/five-star.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,12 @@ var demo = demo || {};
stars.slice(hovered + 1, rank).attr("src", imgs.select);
stars.slice(Math.max(hovered, rank), 5).attr("src", imgs.blank);
};

demo.fiveStar.bindChangeListener = function (that) {
// TODO: This will be simplified once FLUID-4258 is implemented
that.applier.modelChanged.addListener("rank", function (newModel) {
demo.fiveStar.updateARIA(that.stars, newModel.rank);
that.refreshView();
});

/** Update all the UI state to reflect a change in rank **/

demo.fiveStar.updateRank = function (that, newRank) {
demo.fiveStar.updateARIA(that.stars, newRank);
that.refreshView();
};

/**
Expand Down Expand Up @@ -109,15 +108,18 @@ var demo = demo || {};
}, {
funcName: "demo.fiveStar.setARIA",
args: "{that}"
}, {
funcName: "demo.fiveStar.bindChangeListener",
args: "{that}"
}]
},
modelListeners: {
"rank": {
funcName: "demo.fiveStar.updateRank",
args: ["{that}", "{change}.value"]
}
},
invokers: {
setRank: {
func: "{that}.applier.requestChange",
args: ["rank", "{arguments}.0"]
changePath: "rank",
value: "{arguments}.0"
},
renderStarState: {
funcName: "demo.fiveStar.renderStarState",
Expand Down
100 changes: 82 additions & 18 deletions src/framework/core/js/DataBinding.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,11 @@ var fluid_1_5 = fluid_1_5 || {};
* @param exact (boolean) Whether the path must exactly match the length of the specification in
* terms of path segments in order to count as match. If exact is falsy, short specifications will
* match all longer paths as if they were padded out with "*" segments
* @return (string) The path which matched the specification, or <code>null</code> if there was no match
* @return (array of string) The path segments which matched the specification, or <code>null</code> if there was no match
*/

fluid.pathUtil.matchPath = function (spec, path, exact) {
var togo = "";
var togo = [];
while (true) {
if (((path === "") ^ (spec === "")) && exact) {
return null;
Expand All @@ -362,7 +362,7 @@ var fluid_1_5 = fluid_1_5 || {};
if (spechead !== "*" && spechead !== pathhead) {
return null;
}
togo = fluid.pathUtil.composePath(togo, pathhead);
togo.push(pathhead);
spec = fluid.pathUtil.getFromHeadPath(spec);
path = fluid.pathUtil.getFromHeadPath(path);
}
Expand Down Expand Up @@ -439,14 +439,75 @@ var fluid_1_5 = fluid_1_5 || {};
* @param [eventName] - optional - the event name to be listened to - defaults to "modelChanged"
* @param [namespace] - optional - the event namespace
*/
fluid.addSourceGuardedListener = function(applier, path, source, func, eventName, namespace) {
fluid.addSourceGuardedListener = function(applier, path, source, func, eventName, namespace, softNamespace) {
eventName = eventName || "modelChanged";
applier[eventName].addListener(path,
function() {
if (!applier.hasChangeSource(source)) {
func.apply(null, arguments);
}
}, namespace);
var wrapped = function () {
if (!applier.hasChangeSource(source)) {
return func.apply(null, arguments);
}
};
fluid.event.impersonateListener(func, wrapped);
applier[eventName].addListener(path, wrapped, namespace, softNamespace);
};

// unsupported, NON-API function
fluid.resolveModelListener = function (that, record) {
var togo = function (newModel, oldModel, changes, path) {
var change = {
value: fluid.get(newModel, path),
oldValue: fluid.get(oldModel, path),
path: path
};
var args = [change];
var localRecord = {change: change, arguments: args};
if (record.args) {
args = fluid.expandOptions(record.args, that, {}, localRecord);
}
fluid.event.invokeListener(record.listener, fluid.makeArray(args));
};
fluid.event.impersonateListener(record.listener, togo);
return togo;
};

var modelPrefix = "model.";

fluid.resolveModelReference = function (that, path) {
if (path.charAt(0) === "{") {
var parsed = fluid.parseContextReference(path);
var context = fluid.resolveContext(parsed.context, that);
if (!context || !context.applier) {
fluid.fail("Cannot look up model reference " + path + " to a model component with applier");
}
if (parsed.path.indexOf(modelPrefix) !== 0) {
fluid.fail("Path in model reference " + path + " must begin with \"model.\"");
}
return {
that: context,
path: parsed.path.substring(modelPrefix.length)
};
} else return {
that: that,
path: path
};
};

// unsupported, NON-API function
fluid.mergeModelListeners = function (that, listeners) {
fluid.each(listeners, function (value, path) {
if (typeof(value) === "string") {
value = {
funcName: value
};
}
var records = fluid.event.resolveListenerRecord(value, that, "modelListeners", null, false);
var parsed = fluid.resolveModelReference(that, path);
// Bypass fluid.event.dispatchListener by means of "standard = false" and enter our custom workflow including expanding "change":
fluid.each(records.records, function (record) {
var func = fluid.resolveModelListener(that, record);
fluid.addSourceGuardedListener(parsed.that.applier, parsed.path, record.guardSource, func, "modelChanged", record.namespace, record.softNamespace);
fluid.recordListener(parsed.that.applier.modelChanged, func, fluid.shadowForComponent(that));
});
});
};

/** Convenience method to fire a change event to a specified applier, including
Expand Down Expand Up @@ -515,7 +576,7 @@ var fluid_1_5 = fluid_1_5 || {};
var that = {
// For now, we don't use "id" to avoid confusing component detection which uses
// a simple algorithm looking for that field
changeid: fluid.allocateGuid(),
applierid: fluid.allocateGuid(),
model: model
};

Expand Down Expand Up @@ -564,7 +625,7 @@ var fluid_1_5 = fluid_1_5 || {};
var match = fluid.pathUtil.matchPath(pathSpec, changePath);
if (match !== null) {
var record = {
changePath: changePath,
match: match,
pathSpec: pathSpec,
listener: listener,
priority: priority,
Expand Down Expand Up @@ -622,8 +683,8 @@ var fluid_1_5 = fluid_1_5 || {};

function adaptListener(that, name) {
that[name] = {
addListener: function (spec, listener, namespace) {
baseEvents[name].addListener(wrapListener(listener, spec), namespace);
addListener: function (spec, listener, namespace, softNamespace) {
baseEvents[name].addListener(wrapListener(listener, spec), namespace, null, null, softNamespace);
},
removeListener: function (listener) {
baseEvents[name].removeListener(listener);
Expand Down Expand Up @@ -682,23 +743,26 @@ var fluid_1_5 = fluid_1_5 || {};
fluid.model.copyModel(oldModel, model);
}
fluid.model.applyChangeRequest(model, changeRequest, options.resolverSetConfig);
fireEvent("modelChanged", changeRequest.path, [model, oldModel, [changeRequest]]);
fireAgglomerated("modelChanged", "all", [changeRequest], [model, oldModel, null, null], 2, 3);
}
};

that.fireChangeRequest = sourceWrapModelChanged(that.fireChangeRequest, threadLocal);
fluid.bindRequestChange(that);

function fireAgglomerated(eventName, formName, changes, args, accpos) {
function fireAgglomerated(eventName, formName, changes, args, accpos, matchpos) {
var fireSpec = makeFireSpec();
for (var i = 0; i < changes.length; ++i) {
prepareFireEvent(eventName, changes[i].path, fireSpec, changes[i]);
}
for (var j = 0; j < fireSpec[formName].length; ++j) {
var spec = fireSpec[formName][j];
if (accpos) {
if (accpos !== undefined) {
args[accpos] = spec.accumulate;
}
if (matchpos !== undefined) {
args[matchpos] = spec.match;
}
var ret = spec.listener.apply(null, args);
if (ret === false) {
return false;
Expand Down Expand Up @@ -735,7 +799,7 @@ var fluid_1_5 = fluid_1_5 || {};
fluid.clear(model);
fluid.model.copyModel(model, newModel);
}
fireAgglomerated("modelChanged", "all", changes, [model, oldModel, null], 2);
fireAgglomerated("modelChanged", "all", changes, [model, oldModel, null, null], 2, 3);
},
fireChangeRequest: function (changeRequest) {
preFireChangeRequest(changeRequest);
Expand Down
67 changes: 43 additions & 24 deletions src/framework/core/js/Fluid.js
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,14 @@ var fluid = fluid || fluid_1_5;
});
return togo.sort(fluid.priorityComparator);
};

// unsupported, non-API function
fluid.event.invokeListener = function (listener, args) {
if (typeof(listener) === "string") {
listener = fluid.event.resolveListener({globalName: listener}); // just resolves globals
}
return listener.apply(null, args);
};

// unsupported, NON-API function
fluid.event.resolveListener = function (listener) {
Expand Down Expand Up @@ -1202,15 +1210,15 @@ var fluid = fluid || fluid_1_5;
return { records: records };
};

fluid.expandOptions = function (material) {
fluid.fail("fluid.expandOptions could not be loaded - please include FluidIoC.js in order to operate IoC-driven event with descriptor " + material);
};

// unsupported, NON-API function
fluid.mergeListeners = function (that, events, listeners) {
fluid.each(listeners, function (value, key) {
var firer, namespace;
if (key.charAt(0) === "{") {
if (!fluid.expandOptions) {
fluid.fail("fluid.expandOptions could not be loaded - please include FluidIoC.js in order to operate IoC-driven event with descriptor " +
key);
}
firer = fluid.expandOptions(key, that);
if (!firer) {
fluid.fail("Error in listener record: key " + key + " could not be looked up to an event firer - did you miss out \"events.\" when referring to an event firer?");
Expand All @@ -1227,7 +1235,7 @@ var fluid = fluid || fluid_1_5;
}
firer = events[key];
}
var record = fluid.event.resolveListenerRecord(value, that, key, namespace);
var record = fluid.event.resolveListenerRecord(value, that, key, namespace, true);
fluid.event.addListenerToFirer(firer, record.records, namespace, record.adderWrapper);
});
};
Expand Down Expand Up @@ -1255,19 +1263,23 @@ var fluid = fluid || fluid_1_5;
that.events[eventKey] = fluid.eventFromRecord(eventSpec, eventKey, that);
});
};


// unsupported, NON-API function
fluid.mergeListenerPolicy = function (target, source, key) {
// cf. triage in mergeListeners
var hasNamespace = key.charAt(0) !== "{" && key.indexOf(".") !== -1;
return hasNamespace ? (source || target) : fluid.makeArray(target).concat(fluid.makeArray(source));
return hasNamespace ? (source || target) : fluid.arrayConcatPolicy(target, source);
};

fluid.mergeListenersPolicy = function (target, source) {
target = target || {};
fluid.each(source, function (listeners, key) {
target[key] = fluid.mergeListenerPolicy(target[key], listeners, key);
});
return target;

// unsupported, NON-API function
fluid.makeMergeListenersPolicy = function (merger) {
return function (target, source) {
target = target || {};
fluid.each(source, function (listeners, key) {
target[key] = merger(target[key], listeners, key);
});
return target;
};
};

/** Removes duplicated and empty elements from an already sorted array **/
Expand Down Expand Up @@ -1455,7 +1467,8 @@ var fluid = fluid || fluid_1_5;
fluid.popActivity();
}
};


// unsupported, NON-API function
fluid.doIndexDefaults = function (defaultName, defaults, index, indexSpec) {
var requiredGrades = fluid.makeArray(indexSpec.gradeNames);
for (var i = 0; i < requiredGrades.length; ++ i) {
Expand Down Expand Up @@ -1817,7 +1830,7 @@ var fluid = fluid || fluid_1_5;
options.initter = function () {
// This hack is necessary to ensure that the FINAL evaluation doesn't balk when discovering a trunk path which was already
// visited during self-driving via the expander. This bi-modality is sort of rubbish, but we currently don't have "room"
// in the strategy API to express when full evaluation is required - and the "flooding API" is not standardised.
// in the strategy API to express when full evaluation is required - and the "flooding API" is not standardised. See FLUID-4930
options.evaluateFully = true;
fluid.fetchMergeChildren(options.target, 0, [], options.sources, options.mergePolicy, options);
};
Expand Down Expand Up @@ -2004,7 +2017,7 @@ var fluid = fluid || fluid_1_5;
onDestroy: null
},
mergePolicy: {
listeners: fluid.mergeListenersPolicy
listeners: fluid.makeMergeListenersPolicy(fluid.mergeListenerPolicy)
}
});

Expand All @@ -2022,10 +2035,19 @@ var fluid = fluid || fluid_1_5;
},
mergePolicy: {
model: "preserve",
applier: "nomerge"
applier: "nomerge",
modelListeners: fluid.makeMergeListenersPolicy(fluid.arrayConcatPolicy)
}
});

fluid.defaults("fluid.modelRelayComponent", {
gradeNames: ["fluid.modelComponent", "fluid.eventedComponent", "autoInit"]
});

fluid.defaults("fluid.standardComponent", {
gradeNames: ["fluid.modelRelayComponent", "autoInit"]
});

/** A special "marker object" which is recognised as one of the arguments to
* fluid.initSubcomponents. This object is recognised by reference equality -
* where it is found, it is replaced in the actual argument position supplied
Expand Down Expand Up @@ -2110,6 +2132,9 @@ var fluid = fluid || fluid_1_5;
if (evented) {
fluid.instantiateFirers(that, options);
fluid.mergeListeners(that, that.events, options.listeners);
if (fluid.mergeModelListeners) {
fluid.mergeModelListeners(that, options.modelListeners);
}
}
if (!fluid.hasGrade(options, "autoInit")) {
fluid.clearLifecycleFunctions(options);
Expand Down Expand Up @@ -2207,12 +2232,6 @@ var fluid = fluid || fluid_1_5;
} else {
togo = entry.apply(null, args);
}

// TODO: deprecate "returnedOptions" and incorporate into regular ginger world system
var returnedOptions = togo ? togo.returnedOptions : null;
if (returnedOptions && returnedOptions.listeners) {
fluid.mergeListeners(that, that.events, returnedOptions.listeners);
}
return togo;
};

Expand Down
Loading

0 comments on commit 74e0503

Please sign in to comment.