this.helperFunction = function helperFunc(params, hash, options, env) { var param; var handlebarsOptions = {}; merge(handlebarsOptions, options); merge(handlebarsOptions, env); handlebarsOptions.hash = {}; for (var prop in hash) { param = hash[prop]; if (isStream(param)) { handlebarsOptions.hash[prop] = param._label; } else { handlebarsOptions.hash[prop] = param; } } var args = new Array(params.length); for (var i = 0, l = params.length; i < l; i++) { param = params[i]; if (isStream(param)) { args[i] = param._label; } else { args[i] = param; } } args.push(handlebarsOptions); return fn.apply(this, args); };
function makeBindings(hash, options, view) { for (var prop in hash) { var value = hash[prop]; // Classes are processed separately if (prop === 'class' && isStream(value)) { hash.classBinding = value._label; delete hash['class']; continue; } if (prop === 'classBinding') { continue; } if (IS_BINDING.test(prop)) { if (isStream(value)) { Ember.warn("You're attempting to render a view by passing " + prop + " " + "to a view helper without a quoted value, " + "but this syntax is ambiguous. You should either surround " + prop + "'s value in quotes or remove `Binding` " + "from " + prop + "."); } else if (typeof value === 'string') { hash[prop] = view._getBindingForStream(value); } } else { if (isStream(value) && prop !== 'id') { hash[prop + 'Binding'] = view._getBindingForStream(value); delete hash[prop]; } } } }
/** The `{{link-to}}` helper renders a link to the supplied `routeName` passing an optionally supplied model to the route as its `model` context of the route. The block for `{{link-to}}` becomes the innerHTML of the rendered element: ```handlebars {{#link-to 'photoGallery'}} Great Hamster Photos {{/link-to}} ``` You can also use an inline form of `{{link-to}}` helper by passing the link text as the first argument to the helper: ```handlebars {{link-to 'Great Hamster Photos' 'photoGallery'}} ``` Both will result in: ```html <a href="/hamster-photos"> Great Hamster Photos </a> ``` ### Supplying a tagName By default `{{link-to}}` renders an `<a>` element. This can be overridden for a single use of `{{link-to}}` by supplying a `tagName` option: ```handlebars {{#link-to 'photoGallery' tagName="li"}} Great Hamster Photos {{/link-to}} ``` ```html <li> Great Hamster Photos </li> ``` To override this option for your entire application, see "Overriding Application-wide Defaults". ### Disabling the `link-to` helper By default `{{link-to}}` is enabled. any passed value to `disabled` helper property will disable the `link-to` helper. static use: the `disabled` option: ```handlebars {{#link-to 'photoGallery' disabled=true}} Great Hamster Photos {{/link-to}} ``` dynamic use: the `disabledWhen` option: ```handlebars {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} Great Hamster Photos {{/link-to}} ``` any passed value to `disabled` will disable it except `undefined`. to ensure that only `true` disable the `link-to` helper you can override the global behaviour of `Ember.LinkView`. ```javascript Ember.LinkView.reopen({ disabled: Ember.computed(function(key, value) { if (value !== undefined) { this.set('_isDisabled', value === true); } return value === true ? get(this, 'disabledClass') : false; }) }); ``` see "Overriding Application-wide Defaults" for more. ### Handling `href` `{{link-to}}` will use your application's Router to fill the element's `href` property with a url that matches the path to the supplied `routeName` for your router's configured `Location` scheme, which defaults to Ember.HashLocation. ### Handling current route `{{link-to}}` will apply a CSS class name of 'active' when the application's current route matches the supplied routeName. For example, if the application's current route is 'photoGallery.recent' the following use of `{{link-to}}`: ```handlebars {{#link-to 'photoGallery.recent'}} Great Hamster Photos from the last week {{/link-to}} ``` will result in ```html <a href="/hamster-photos/this-week" class="active"> Great Hamster Photos </a> ``` The CSS class name used for active classes can be customized for a single use of `{{link-to}}` by passing an `activeClass` option: ```handlebars {{#link-to 'photoGallery.recent' activeClass="current-url"}} Great Hamster Photos from the last week {{/link-to}} ``` ```html <a href="/hamster-photos/this-week" class="current-url"> Great Hamster Photos </a> ``` To override this option for your entire application, see "Overriding Application-wide Defaults". ### Supplying a model An optional model argument can be used for routes whose paths contain dynamic segments. This argument will become the model context of the linked route: ```javascript App.Router.map(function() { this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); }); ``` ```handlebars {{#link-to 'photoGallery' aPhoto}} {{aPhoto.title}} {{/link-to}} ``` ```html <a href="/hamster-photos/42"> Tomster </a> ``` ### Supplying multiple models For deep-linking to route paths that contain multiple dynamic segments, multiple model arguments can be used. As the router transitions through the route path, each supplied model argument will become the context for the route with the dynamic segments: ```javascript App.Router.map(function() { this.resource("photoGallery", {path: "hamster-photos/:photo_id"}, function() { this.route("comment", {path: "comments/:comment_id"}); }); }); ``` This argument will become the model context of the linked route: ```handlebars {{#link-to 'photoGallery.comment' aPhoto comment}} {{comment.body}} {{/link-to}} ``` ```html <a href="/hamster-photos/42/comment/718"> A+++ would snuggle again. </a> ``` ### Supplying an explicit dynamic segment value If you don't have a model object available to pass to `{{link-to}}`, an optional string or integer argument can be passed for routes whose paths contain dynamic segments. This argument will become the value of the dynamic segment: ```javascript App.Router.map(function() { this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); }); ``` ```handlebars {{#link-to 'photoGallery' aPhotoId}} {{aPhoto.title}} {{/link-to}} ``` ```html <a href="/hamster-photos/42"> Tomster </a> ``` When transitioning into the linked route, the `model` hook will be triggered with parameters including this passed identifier. ### Allowing Default Action By default the `{{link-to}}` helper prevents the default browser action by calling `preventDefault()` as this sort of action bubbling is normally handled internally and we do not want to take the browser to a new URL (for example). If you need to override this behavior specify `preventDefault=false` in your template: ```handlebars {{#link-to 'photoGallery' aPhotoId preventDefault=false}} {{aPhotoId.title}} {{/link-to}} ``` ### Overriding attributes You can override any given property of the Ember.LinkView that is generated by the `{{link-to}}` helper by passing key/value pairs, like so: ```handlebars {{#link-to aPhoto tagName='li' title='Following this link will change your life' classNames='pic sweet'}} Uh-mazing! {{/link-to}} ``` See [Ember.LinkView](/api/classes/Ember.LinkView.html) for a complete list of overrideable properties. Be sure to also check out inherited properties of `LinkView`. ### Overriding Application-wide Defaults ``{{link-to}}`` creates an instance of Ember.LinkView for rendering. To override options for your entire application, reopen Ember.LinkView and supply the desired values: ``` javascript Ember.LinkView.reopen({ activeClass: "is-active", tagName: 'li' }) ``` It is also possible to override the default event in this manner: ``` javascript Ember.LinkView.reopen({ eventName: 'customEventName' }); ``` @method link-to @for Ember.Handlebars.helpers @param {String} routeName @param {Object} [context]* @param [options] {Object} Handlebars key/value pairs of options, you can override any property of Ember.LinkView @return {String} HTML string @see {Ember.LinkView} */ function linkToHelper(params, hash, options, env) { var queryParamsObject; Ember.assert("You must provide one or more parameters to the link-to helper.", params.length); var lastParam = params[params.length - 1]; if (lastParam && lastParam.isQueryParams) { hash.queryParamsObject = queryParamsObject = params.pop(); } if (hash.disabledWhen) { hash.disabled = hash.disabledWhen; delete hash.disabledWhen; } if (!options.template) { var linkTitle = params.shift(); var parseTextAsHTML = options.morph.parseTextAsHTML; if (isStream(linkTitle)) { hash.linkTitle = { stream: linkTitle }; } options.template = { isHTMLBars: true, revision: 'Ember@VERSION_STRING_PLACEHOLDER', render: function(view, env) { var value = read(linkTitle) || ""; if (parseTextAsHTML) { return value; } else { return env.dom.createTextNode(value); } } }; } for (var i = 0; i < params.length; i++) { if (isStream(params[i])) { var lazyValue = params[i]; if (!lazyValue._isController) { while (ControllerMixin.detect(lazyValue.value())) { lazyValue = lazyValue.get('model'); } } params[i] = lazyValue; } } hash.params = params; options.helperName = options.helperName || 'link-to'; return env.helpers.view.helperFunction.call(this, [LinkView], hash, options, env); }
this.helperFunction = function helperFunc(params, hash, options, env) { var param, blockResult, fnResult; var context = this; var handlebarsOptions = { hash: { }, types: new Array(params.length), hashTypes: { } }; merge(handlebarsOptions, options); merge(handlebarsOptions, env); handlebarsOptions.hash = {}; if (options.isBlock) { handlebarsOptions.fn = function() { blockResult = options.template.render(context, env, options.morph.contextualElement); }; } for (var prop in hash) { param = hash[prop]; handlebarsOptions.hashTypes[prop] = calculateCompatType(param); if (isStream(param)) { handlebarsOptions.hash[prop] = param._label; } else { handlebarsOptions.hash[prop] = param; } } var args = new Array(params.length); for (var i = 0, l = params.length; i < l; i++) { param = params[i]; handlebarsOptions.types[i] = calculateCompatType(param); if (isStream(param)) { args[i] = param._label; } else { args[i] = param; } } args.push(handlebarsOptions); fnResult = fn.apply(this, args); return options.isBlock ? blockResult : fnResult; };
export function actionHelper(params, hash, options, env) { var target; if (!hash.target) { target = this.getStream('controller'); } else if (isStream(hash.target)) { target = hash.target; } else { target = this.getStream(hash.target); } // Ember.assert("You specified a quoteless path to the {{action}} helper which did not resolve to an action name (a string). Perhaps you meant to use a quoted actionName? (e.g. {{action 'save'}}).", !params[0].isStream); // Ember.deprecate("You specified a quoteless path to the {{action}} helper which did not resolve to an action name (a string). Perhaps you meant to use a quoted actionName? (e.g. {{action 'save'}}).", params[0].isStream); var actionOptions = { eventName: hash.on || "click", parameters: params.slice(1), view: this, bubbles: hash.bubbles, preventDefault: hash.preventDefault, target: target, withKeyCode: hash.withKeyCode }; var actionId = ActionHelper.registerAction(params[0], actionOptions, hash.allowedKeys); env.dom.setAttribute(options.element, 'data-ember-action', actionId); }
function buildDynamicKeyStream(source, keySource) { if (!isStream(keySource)) { return source.get(keySource); } else { return new DynamicKeyStream(source, keySource); } }
var DynamicKeyStream = function DynamicKeyStream(source, keySource) { if (!isStream(keySource)) { return new KeyStream(source, keySource); } Ember.assert('DynamicKeyStream error: source must be a stream', isStream(source)); // TODO: This isn't necessary. // used to get the original path for debugging and legacy purposes var label = labelFor(source, keySource); this.init(label); this.path = label; this.sourceDep = this.addMutableDependency(source); this.keyDep = this.addMutableDependency(keySource); this.observedObject = null; this.observedKey = null; };
export function componentHelper(params, hash, options, env) { Ember.assert( "The `component` helper expects exactly one argument, plus name/property values.", params.length === 1 ); var componentNameParam = params[0]; var container = this.container || read(this._keywords.view).container; var props = { helperName: options.helperName || 'component' }; if (options.template) { props.template = options.template; } var viewClass; if (isStream(componentNameParam)) { viewClass = BoundComponentView; props = { _boundComponentOptions: Ember.merge(hash, props) }; props._boundComponentOptions.componentNameStream = componentNameParam; } else { viewClass = readComponentFactory(componentNameParam, container); if (!viewClass) { throw new EmberError('HTMLBars error: Could not find component named "' + componentNameParam + '".'); } mergeViewBindings(this, props, hash); } appendTemplatedView(this, options.morph, viewClass, props); }
function appendBlockConditional(view, inverted, helperName, params, hash, options, env) { Ember.assert( "The block form of the `if` and `unless` helpers expect exactly one " + "argument, e.g. `{{#if newMessages}} You have new messages. {{/if}}.`", params.length === 1 ); var condition = shouldDisplay(params[0]); var truthyTemplate = (inverted ? options.inverse : options.template) || emptyTemplate; var falsyTemplate = (inverted ? options.template : options.inverse) || emptyTemplate; if (isStream(condition)) { view.appendChild(BoundIfView, { _morph: options.morph, _context: get(view, 'context'), conditionStream: condition, truthyTemplate: truthyTemplate, falsyTemplate: falsyTemplate, helperName: helperName }); } else { var template = condition ? truthyTemplate : falsyTemplate; if (template) { return template.render(view, env, options.morph.contextualElement); } } }
function getKey(obj, key) { if (isStream(obj)) { return obj.getKey(key); } else { return obj && obj[key]; } }
/** @module ember @submodule ember-htmlbars */ //import Helper from "ember-htmlbars/system/helper"; /** A helper function used by `registerBoundHelper`. Takes the provided Handlebars helper function fn and returns it in wrapped bound helper form. The main use case for using this outside of `registerBoundHelper` is for registering helpers on the container: ```js var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { return word.toUpperCase(); }); container.register('helper:my-bound-helper', boundHelperFn); ``` In the above example, if the helper function hadn't been wrapped in `makeBoundHelper`, the registered helper would be unbound. @method makeBoundHelper @for Ember.Handlebars @param {Function} function @param {String} dependentKeys* @since 1.2.0 @deprecated */ export default function makeBoundHelper(fn, ...dependentKeys) { return { _dependentKeys: dependentKeys, isHandlebarsCompat: true, isHTMLBars: true, helperFunction(params, hash, templates) { Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !templates.template.yield); var args = readArray(params); var properties = new Array(params.length); for (var i = 0, l = params.length; i < l; i++) { var param = params[i]; if (isStream(param)) { properties[i] = param.label; } else { properties[i] = param; } } args.push({ hash: readHash(hash) , templates, data: { properties } }); return fn.apply(undefined, args); } }; }
/* Given a path name, returns whether or not a component with that name was found in the container. */ export default function isComponent(env, scope, path) { let owner = env.owner; if (!owner) { return false; } if (typeof path === 'string') { if (CONTAINS_DOT_CACHE.get(path)) { let stream = env.hooks.get(env, scope, path); if (isStream(stream)) { let cell = stream.value(); if (isComponentCell(cell)) { return true; } } } if (!CONTAINS_DASH_CACHE.get(path)) { return false; } if (hasComponentOrTemplate(owner, path)) { return true; // global component found } else { if (isEnabled('ember-htmlbars-local-lookup')) { let moduleName = env.meta && env.meta.moduleName; if (!moduleName) { // Without a source moduleName, we can not perform local lookups. return false; } let options = { source: `template:${moduleName}` }; return hasComponentOrTemplate(owner, path, options); } else { return false; } } } }
Stream.wrap = function(value, Kind, param) { if (isStream(value)) { return value; } else { return new Kind(value, param); } };
function mutParam(read, stream, internal) { if (internal) { if (!isStream(stream)) { let literal = stream; stream = new LiteralStream(literal); } } else { assert('You can only pass a path to mut', isStream(stream)); } if (stream[MUTABLE_REFERENCE]) { return stream; } return new MutStream(stream); }
/** @module ember-metal */ /** @private @class Dependency @namespace Ember.streams @constructor */ function Dependency(depender, dependee) { Ember.assert("Dependency error: Depender must be a stream", isStream(depender)); this.next = null; this.prev = null; this.depender = depender; this.dependee = dependee; this.unsubscription = null; }
QUnit.test('returns a stream if a stream is in the array', function(assert) { let stream = new Stream(function() { return 'bar'; }); let result = concat(['foo', stream, 'baz'], ' '); assert.ok(isStream(result), 'a stream is returned'); assert.equal(read(result), 'foo bar baz'); });
function calculateCompatType(item) { if (isStream(item)) { return 'ID'; } else { var itemType = typeof item; return itemType.toUpperCase(); } }
setSource: function(nextSource) { Ember.assert("KeyStream error: source must be an object", typeof nextSource === 'object'); var prevSource = this.source; if (nextSource !== prevSource) { if (isStream(prevSource)) { prevSource.unsubscribe(this._didChange, this); } if (isStream(nextSource)) { nextSource.subscribe(this._didChange, this); } this.source = nextSource; this.notify(); } },
function mergeGenericViewBindings(view, props, hash) { for (var key in hash) { if (key === 'id' || key === 'tag' || key === 'class' || key === 'classBinding' || key === 'classNameBindings' || key === 'attributeBindings') { continue; } var value = hash[key]; if (IS_BINDING.test(key)) { if (typeof value === 'string') { Ember.deprecate( "You're attempting to render a view by passing " + key + " " + "to a view helper, but this syntax is deprecated. You should use `" + key.slice(0, -7) + "=someValue` instead." ); props[key] = view._getBindingForStream(value); } else if (isStream(value)) { Ember.deprecate( "You're attempting to render a view by passing " + key + " " + "to a view helper without a quoted value, but this syntax is " + "ambiguous. You should either surround " + key + "'s value in " + "quotes or remove `Binding` from " + key + "." ); props[key] = view._getBindingForStream(value); } else { props[key] = value; } } else { if (isStream(value)) { props[key + 'Binding'] = view._getBindingForStream(value); } else { props[key] = value; } } } }
const buildStream = function buildStream(params) { const [objRef, pathRef] = params; Ember.assert('The first argument to {{get}} must be a stream', isStream(objRef)); Ember.assert('{{get}} requires at least two arguments', params.length > 1); const stream = new DynamicKeyStream(objRef, pathRef); return stream; };
function mutParam(read, stream, internal) { if (internal) { if (!isStream(stream)) { let literal = stream; stream = new Stream(function() { return literal; }, `(literal ${literal})`); stream.setValue = function(newValue) { literal = newValue; stream.notify(); }; } } else { Ember.assert("You can only pass a path to mut", isStream(stream)); } if (stream[MUTABLE_REFERENCE]) { return stream; } return new MutStream(stream); }
export function partialHelper(params, hash, options, env) { options.helperName = options.helperName || 'partial'; var name = params[0]; if (isStream(name)) { options.template = createPartialTemplate(name); bind.call(this, name, hash, options, env, true, exists); } else { return renderPartial(name, this, env, options.morph.contextualElement); } }
function ShouldDisplayStream(predicate) { Ember.assert('ShouldDisplayStream error: predicate must be a stream', isStream(predicate)); var isTruthy = predicate.get('isTruthy'); this.init(); this.predicate = predicate; this.isTruthy = isTruthy; this.lengthDep = null; this.addDependency(predicate); this.addDependency(isTruthy); }
export default function inline(env, morph, view, path, params, hash) { var helper = lookupHelper(path, view, env); Ember.assert("A helper named '"+path+"' could not be found", helper); var result = helper.helperFunction.call(undefined, params, hash, { morph: morph }, env); if (isStream(result)) { appendSimpleBoundView(view, morph, result); } else { morph.setContent(result); } }
function KeyStream(source, key) { Ember.assert("KeyStream error: key must be a non-empty string", typeof key === 'string' && key.length > 0); Ember.assert("KeyStream error: key must not have a '.'", key.indexOf('.') === -1); this.init(); this.source = source; this.obj = undefined; this.key = key; if (isStream(source)) { source.subscribe(this._didChange, this); } }
// Binds a property into the DOM. This will create a hook in DOM that the // KVO system will look for and update if the property changes. function bind(property, hash, options, env, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) { var valueStream = isStream(property) ? property : this.getStream(property); var lazyValue; if (childProperties) { lazyValue = new SimpleStream(valueStream); var subscriber = function(childStream) { childStream.value(); lazyValue.notify(); }; for (var i = 0; i < childProperties.length; i++) { var childStream = valueStream.get(childProperties[i]); childStream.value(); childStream.subscribe(subscriber); } } else { lazyValue = valueStream; } // Set up observers for observable objects var viewClass = _viewClass || BoundView; var viewOptions = { _morph: options.morph, preserveContext: preserveContext, shouldDisplayFunc: shouldDisplay, valueNormalizerFunc: valueNormalizer, displayTemplate: options.template, inverseTemplate: options.inverse, lazyValue: lazyValue, previousContext: get(this, 'context'), templateHash: hash, helperName: options.helperName }; if (options.keywords) { viewOptions._keywords = options.keywords; } // Create the view that will wrap the output of this template/property // and add it to the nearest view's childViews array. // See the documentation of Ember._BoundView for more. var bindView = this.createChildView(viewClass, viewOptions); this.appendChild(bindView); lazyValue.subscribe(this._wrapAsScheduled(function() { run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); })); }
/** @private Use the `unboundIf` helper to create a conditional that evaluates once. ```handlebars {{#unboundIf "content.shouldDisplayTitle"}} {{content.title}} {{/unboundIf}} ``` @method unboundIf @for Ember.Handlebars.helpers @param {String} property Property to bind @param {Function} fn Context to provide for rendering @return {String} HTML string @since 1.4.0 */ function unboundIfHelper(params, hash, options, env) { var template = options.template; var value = params[0]; if (isStream(params[0])) { value = params[0].value(); } if (!shouldDisplayIfHelperContent(value)) { template = options.inverse; } return template.render(this, env, options.morph.contextualElement); }
function KeyStream(source, key) { Ember.assert("KeyStream error: source must be a stream", isStream(source)); // TODO: This isn't necessary. Ember.assert("KeyStream error: key must be a non-empty string", typeof key === 'string' && key.length > 0); Ember.assert("KeyStream error: key must not have a '.'", key.indexOf('.') === -1); // used to get the original path for debugging and legacy purposes var label = labelFor(source, key); this.init(label); this.path = label; this.sourceDep = this.addMutableDependency(source); this.observedObject = null; this.key = key; }
export default function attribute(env, morph, element, attrName, attrValue) { if (boundAttributesEnabled) { var attrNode = new AttrNode(attrName, attrValue); attrNode._morph = morph; env.data.view.appendChild(attrNode); } else { if (isStream(attrValue)) { throw new EmberError('Bound attributes are not yet supported in Ember.js'); } else { var sanitizedValue = sanitizeAttributeValue(element, attrName, attrValue); env.dom.setProperty(element, attrName, sanitizedValue); } } }
export default function shouldDisplay(predicate) { if (isStream(predicate)) { return new ShouldDisplayStream(predicate); } var truthy = predicate && get(predicate, 'isTruthy'); if (typeof truthy === 'boolean') { return truthy; } if (isArray(predicate)) { return get(predicate, 'length') !== 0; } else { return !!predicate; } }