Example #1
0
/**
  `bind-attr` allows you to create a binding between DOM element attributes and
  Ember objects. For example:

  ```handlebars
  <img {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  The above handlebars template will fill the `<img>`'s `src` attribute with
  the value of the property referenced with `imageUrl` and its `alt`
  attribute with the value of the property referenced with `imageTitle`.

  If the rendering context of this template is the following object:

  ```javascript
  {
    imageUrl: 'http://lolcats.info/haz-a-funny',
    imageTitle: 'A humorous image of a cat'
  }
  ```

  The resulting HTML output will be:

  ```html
  <img src="http://lolcats.info/haz-a-funny" alt="A humorous image of a cat">
  ```

  `bind-attr` cannot redeclare existing DOM element attributes. The use of `src`
  in the following `bind-attr` example will be ignored and the hard coded value
  of `src="/failwhale.gif"` will take precedence:

  ```handlebars
  <img src="/failwhale.gif" {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  ### `bind-attr` and the `class` attribute

  `bind-attr` supports a special syntax for handling a number of cases unique
  to the `class` DOM element attribute. The `class` attribute combines
  multiple discrete values into a single attribute as a space-delimited
  list of strings. Each string can be:

  * a string return value of an object's property.
  * a boolean return value of an object's property
  * a hard-coded value

  A string return value works identically to other uses of `bind-attr`. The
  return value of the property will become the value of the attribute. For
  example, the following view and template:

  ```javascript
    AView = View.extend({
      someProperty: function() {
        return "aValue";
      }.property()
    })
  ```

  ```handlebars
  <img {{bind-attr class=view.someProperty}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="aValue">
  ```

  A boolean return value will insert a specified class name if the property
  returns `true` and remove the class name if the property returns `false`.

  A class name is provided via the syntax
  `somePropertyName:class-name-if-true`.

  ```javascript
  AView = View.extend({
    someBool: true
  })
  ```

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true"}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="class-name-if-true">
  ```

  An additional section of the binding can be provided if you want to
  replace the existing class instead of removing it when the boolean
  value changes:

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true:class-name-if-false"}}>
  ```

  A hard-coded value can be used by prepending `:` to the desired
  class name: `:class-name-to-always-apply`.

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply"}}>
  ```

  Results in the following rendered output:

  ```html
  <img class="class-name-to-always-apply">
  ```

  All three strategies - string return value, boolean return value, and
  hard-coded value – can be combined in a single declaration:

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply view.someBool:class-name-if-true view.someProperty"}}>
  ```

  @method bind-attr
  @for Ember.Handlebars.helpers
  @param {Hash} options
  @return {String} HTML string
*/
function bindAttrHelper(params, hash, options, env) {
  var element = options.element;

  Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length);

  var view = this;

  // Handle classes differently, as we can bind multiple classes
  var classBindings = hash['class'];
  if (classBindings != null) {
    var attrValue = streamifyClassBindings(view, classBindings);
    new QuotedClassAttrNode(element, 'class', attrValue, env.dom);
    delete hash['class'];
  }

  var attrKeys = keys(hash);

  var attr, path, lazyValue;
  for (var i=0, l=attrKeys.length;i<l;i++) {
    attr = attrKeys[i];
    path = hash[attr];
    if (path.isStream) {
      lazyValue = path;
    } else {
      Ember.assert(
        fmt("You must provide an expression as the value of bound attribute." +
            " You specified: %@=%@", [attr, path]),
        typeof path === 'string'
      );
      lazyValue = view.getStream(path);
    }
    new LegacyBindAttrNode(element, attr, lazyValue, env.dom);
  }
}
Example #2
0
/**
  `bind-attr` allows you to create a binding between DOM element attributes and
  Ember objects. For example:

  ```handlebars
  <img {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  The above handlebars template will fill the `<img>`'s `src` attribute with
  the value of the property referenced with `imageUrl` and its `alt`
  attribute with the value of the property referenced with `imageTitle`.

  If the rendering context of this template is the following object:

  ```javascript
  {
    imageUrl: 'http://lolcats.info/haz-a-funny',
    imageTitle: 'A humorous image of a cat'
  }
  ```

  The resulting HTML output will be:

  ```html
  <img src="http://lolcats.info/haz-a-funny" alt="A humorous image of a cat">
  ```

  `bind-attr` cannot redeclare existing DOM element attributes. The use of `src`
  in the following `bind-attr` example will be ignored and the hard coded value
  of `src="/failwhale.gif"` will take precedence:

  ```handlebars
  <img src="/failwhale.gif" {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  ### `bind-attr` and the `class` attribute

  `bind-attr` supports a special syntax for handling a number of cases unique
  to the `class` DOM element attribute. The `class` attribute combines
  multiple discrete values into a single attribute as a space-delimited
  list of strings. Each string can be:

  * a string return value of an object's property.
  * a boolean return value of an object's property
  * a hard-coded value

  A string return value works identically to other uses of `bind-attr`. The
  return value of the property will become the value of the attribute. For
  example, the following view and template:

  ```javascript
    AView = View.extend({
      someProperty: function() {
        return "aValue";
      }.property()
    })
  ```

  ```handlebars
  <img {{bind-attr class=view.someProperty}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="aValue">
  ```

  A boolean return value will insert a specified class name if the property
  returns `true` and remove the class name if the property returns `false`.

  A class name is provided via the syntax
  `somePropertyName:class-name-if-true`.

  ```javascript
  AView = View.extend({
    someBool: true
  })
  ```

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true"}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="class-name-if-true">
  ```

  An additional section of the binding can be provided if you want to
  replace the existing class instead of removing it when the boolean
  value changes:

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true:class-name-if-false"}}>
  ```

  A hard-coded value can be used by prepending `:` to the desired
  class name: `:class-name-to-always-apply`.

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply"}}>
  ```

  Results in the following rendered output:

  ```html
  <img class="class-name-to-always-apply">
  ```

  All three strategies - string return value, boolean return value, and
  hard-coded value – can be combined in a single declaration:

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply view.someBool:class-name-if-true view.someProperty"}}>
  ```

  @method bind-attr
  @for Ember.Handlebars.helpers
  @param {Hash} options
  @return {String} HTML string
*/
function bindAttrHelper(params, hash, options, env) {
  var element  = jQuery(options.element);

  Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length);

  var view = this;

  // Handle classes differently, as we can bind multiple classes
  var classBindings = hash['class'];
  if (classBindings != null) {

    var classResults = bindClasses(element, classBindings, view, options);

    View.applyAttributeBindings(element, 'class', classResults.join(' '));

    delete hash['class'];
  }

  var attrKeys = keys(hash);

  // For each attribute passed, create an observer and emit the
  // current value of the property as an attribute.
  forEach.call(attrKeys, function(attr) {
    var path = hash[attr];

    var lazyValue;

    if (path.isStream) {
      lazyValue = path;
    } else {
      Ember.assert(fmt("You must provide an expression as the value of bound attribute." +
                       " You specified: %@=%@", [attr, path]), typeof path === 'string' || path.isStream);

      lazyValue = view.getStream(path);
    }

    var value = lazyValue.value();
    var type = typeOf(value);

    Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), 
                 value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean');


    lazyValue.subscribe(view._wrapAsScheduled(function applyAttributeBindings() {
      var result = lazyValue.value();

      Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]),
                   result === null || result === undefined || typeof result === 'number' ||
                     typeof result === 'string' || typeof result === 'boolean');

      View.applyAttributeBindings(element, attr, result);
    }));

    if (value && type === 'boolean') {
      value = attr;
    }

    View.applyAttributeBindings(element, attr, value);
  }, this);
}
test("recomputations from `arrayComputed` observers add back dependent keys", function() {
  var meta = metaFor(obj);
  get(obj, 'people');
  var titles;

  equal(meta.deps, undefined, "precond - nobody depends on people'");
  equal(meta.watching.people, undefined, "precond - nobody is watching people");

  titles = get(obj, 'titles');

  deepEqual(titles, ["Kingsguard", "Queen"], "precond - value is correct");

  ok(meta.deps.people !== undefined, "people has dependencies");
  deepEqual(keys(meta.deps.people), ["titles"], "only titles depends on people");
  equal(meta.deps.people.titles, 1, "titles depends on people exactly once");
  equal(meta.watching.people, 2, "people has two watchers: the array listener and titles");

  run(function() {
    set(obj, 'people', Ember.A());
  });

  // Regular CPs are invalidated when their dependent keys change, but array
  // computeds keep refs up to date
  deepEqual(titles, [], "value is correct");
  equal(meta.cache.titles, titles, "value remains cached");
  ok(meta.deps.people !== undefined, "people has dependencies");
  deepEqual(keys(meta.deps.people), ["titles"], "meta.deps.people is unchanged");
  equal(meta.deps.people.titles, 1, "deps.people.titles is unchanged");
  equal(meta.watching.people, 2, "watching.people is unchanged");
});
export function intersect() {
  var args = a_slice.call(arguments);

  args.push({
    initialize(array, changeMeta, instanceMeta) {
      instanceMeta.itemCounts = {};
    },

    addedItem(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfDependentArrays = changeMeta.property._dependentKeys.length;
      var itemCounts = instanceMeta.itemCounts;

      if (!itemCounts[itemGuid]) {
        itemCounts[itemGuid] = {};
      }

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (++itemCounts[itemGuid][dependentGuid] === 1 &&
          numberOfDependentArrays === keys(itemCounts[itemGuid]).length) {
        array.addObject(item);
      }

      return array;
    },

    removedItem(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfArraysItemAppearsIn;
      var itemCounts = instanceMeta.itemCounts;

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (--itemCounts[itemGuid][dependentGuid] === 0) {
        delete itemCounts[itemGuid][dependentGuid];
        numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length;

        if (numberOfArraysItemAppearsIn === 0) {
          delete itemCounts[itemGuid];
        }

        array.removeObject(item);
      }

      return array;
    }
  });

  return arrayComputed.apply(null, args);
}
  QUnit.test('includes helpers in the registry', function() {
    registry.register('helper:t', Helper);
    let result = discoverKnownHelpers(container);
    let helpers = keys(result);

    deepEqual(helpers, ['t'], 'helpers from the registry were known');
  });
    removedItem: function(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuids = getDependentKeyGuids(changeMeta);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfDependentArrays = changeMeta.property._dependentKeys.length;
      var numberOfArraysItemAppearsIn;
      var itemCounts = instanceMeta.itemCounts;

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (--itemCounts[itemGuid][dependentGuid] === 0) {
        delete itemCounts[itemGuid][dependentGuid];
        numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length;

        if (numberOfArraysItemAppearsIn === 0) {
          delete itemCounts[itemGuid];
        }

        array.removeObject(item);
      }

      return array;
    }
Example #7
0
export function helperMissingHelper(path) {
  if (!resolveHelper) {
    resolveHelper = requireModule('ember-handlebars/helpers/binding')['resolveHelper'];
  } // ES6TODO: stupid circular dep

  var error, view = "";

  var options = arguments[arguments.length - 1];

  // due to the issue reported in https://github.com/wycats/handlebars.js/issues/885
  // we must check to see if we have hash arguments manually
  //
  // This should be removed once Handlebars properly calls `blockHelperMissing` when
  // hash arguments are present.
  var hashArgs = keys(options.hash);
  if (options.fn && hashArgs.length === 0) {
    // NOP for block helpers as they are handled by the block helper (when hash arguments are not present)
    return;
  }

  var helper = resolveHelper(options.data.view.container, options.name);

  if (helper) {
    return helper.apply(this, arguments);
  }

  error = "%@ Handlebars error: Could not find property '%@' on object %@.";
  if (options.data) {
    view = options.data.view;
  }
  throw new EmberError(fmt(error, [view, options.name, this]));
}
  QUnit.test('returns empty object when disabled', function() {
    registry.register('helper:t', Helper);

    let result = discoverKnownHelpers(container);
    let helpers = keys(result);

    deepEqual(helpers, [], 'helpers from the registry were known');
  });
Example #9
0
test("should get a key array for property that is named the same as prototype property", function() {
  var object1 = {
    toString: function() {}
  };

  var object2 = keys(object1);

  deepEqual(object2, ['toString']);
});
Example #10
0
  changeProperties(function() {
    var props = keys(properties);
    var propertyName;

    for (var i = 0, l = props.length; i < l; i++) {
      propertyName = props[i];

      set(obj, propertyName, properties[propertyName]);
    }
  });
Example #11
0
test("should get a key array for a specified object", function() {
  var object1 = {};

  object1.names = "Rahul";
  object1.age = "23";
  object1.place = "Mangalore";

  var object2 = keys(object1);

  deepEqual(object2, ['names','age','place']);
});
Example #12
0
QUnit.test("ensure internal properties do not leak", function() {
  var obj = EmberObject.create({
    firstName: 'Joe',
    lastName:  'Black'
  });

  var expectedProperties = ['firstName', 'lastName'];
  var actualProperties   = keys(obj);

  deepEqual(actualProperties, expectedProperties, 'internal properties do not leak');
});
  QUnit.test('includes resolved helpers', function() {
    resolver.knownForType = function() {
      return {
        'helper:f': true
      };
    };

    registry.register('helper:t', Helper);
    let result = discoverKnownHelpers(container);
    let helpers = keys(result);

    deepEqual(helpers, ['t', 'f'], 'helpers from the registry were known');
  });
Example #14
0
  legacyDidReceiveAttrs: on('didReceiveAttrs', function() {
    if (this._isAngleBracket) { return; }

    var keys = objectKeys(this.attrs);

    for (var i=0, l=keys.length; i<l; i++) {
      // Only issue the deprecation if it wasn't already issued when
      // setting attributes initially.
      if (!(keys[i] in this)) {
        this.notifyPropertyChange(keys[i]);
      }
    }
  }),
Example #15
0
function eachDestroyable(container, callback) {
  var cache = container.cache;
  var keys = emberKeys(cache);
  var key, value;

  for (var i = 0, l = keys.length; i < l; i++) {
    key = keys[i];
    value = cache[key];

    if (option(container, key, 'instantiate') !== false) {
      callback(value);
    }
  }
}
Example #16
0
/**
  Merge the contents of two objects together into the first object.

  ```javascript
  Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
  var a = {first: 'Yehuda'};
  var b = {last: 'Katz'};
  Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'}
  ```

  @method merge
  @for Ember
  @param {Object} original The object to merge into
  @param {Object} updates The object to copy properties from
  @return {Object}
*/
export default function merge(original, updates) {
  if (!updates || typeof updates !== 'object') {
    return original;
  }

  var props = keys(updates);
  var prop;
  var length = props.length;

  for (var i = 0; i < length; i++) {
    prop = props[i];
    original[prop] = updates[prop];
  }

  return original;
}
Example #17
0
  loadedParams: computed('resolvedParams', function computeLinkViewRouteArgs() {
    var router = get(this, 'router');
    if (!router) { return; }

    var resolvedParams = get(this, 'resolvedParams');
    var namedRoute = resolvedParams.targetRouteName;

    if (!namedRoute) { return; }

    Ember.assert(fmt("The attempt to link-to route '%@' failed. " +
                     "The router did not find '%@' in its possible routes: '%@'",
                     [namedRoute, namedRoute, keys(router.router.recognizer.names).join("', '")]),
                     router.hasRoute(namedRoute));

    if (!paramsAreLoaded(resolvedParams.models)) { return; }

    return resolvedParams;
  }),
Example #18
0
QUnit.test('default environment values are passed through', function() {
  var keyNames = keys(defaultEnv);
  expect(keyNames.length);

  view = EmberView.create({
    template: {
      isHTMLBars: true,
      revision: 'Ember@VERSION_STRING_PLACEHOLDER',
      render(view, env, contextualElement, blockArguments) {
        for (var i = 0, l = keyNames.length; i < l; i++) {
          var keyName = keyNames[i];

          deepEqual(env[keyName], defaultEnv[keyName], 'passes ' + keyName + ' from the default env');
        }
      }
    }
  });

  runAppend(view);
});
Example #19
0
    addedItem: function(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuids = getDependentKeyGuids(changeMeta);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfDependentArrays = changeMeta.property._dependentKeys.length;
      var itemCounts = instanceMeta.itemCounts;

      if (!itemCounts[itemGuid]) {
        itemCounts[itemGuid] = {};
      }

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (++itemCounts[itemGuid][dependentGuid] === 1 &&
          numberOfDependentArrays === keys(itemCounts[itemGuid]).length) {
        array.addObject(item);
      }

      return array;
    },
export default function discoverKnownHelpers(container) {
  let registry = container && container._registry;
  let helpers = dictionary(null);

  if (isEnabled('ember-htmlbars-dashless-helpers')) {
    if (!registry) {
      return helpers;
    }

    let known = registry.knownForType('helper');
    let knownContainerKeys = keys(known);

    for (let index = 0, length = knownContainerKeys.length; index < length; index++) {
      let fullName = knownContainerKeys[index];
      let name = fullName.slice(7); // remove `helper:` from fullName

      helpers[name] = true;
    }
  }

  return helpers;
}
Example #21
0
 visit('/posts').then(function() {
   equal(keys(logs).length, 0, 'expected no logs');
 });
Example #22
0
 run(function () {
   var keys = objectKeys(objs);
   for (var i = 0, l = keys.length; i < l; i++) {
     objs[keys[i]].destroy();
   }
 });
Example #23
0
  var Class = function() {
    if (!wasApplied) {
      Class.proto(); // prepare prototype...
    }
    o_defineProperty(this, GUID_KEY, nullDescriptor);
    o_defineProperty(this, '__nextSuper', undefinedDescriptor);
    var m = meta(this);
    var proto = m.proto;
    m.proto = this;
    if (initMixins) {
      // capture locally so we can clear the closed over variable
      var mixins = initMixins;
      initMixins = null;
      apply(this, this.reopen, mixins);
    }
    if (initProperties) {
      // capture locally so we can clear the closed over variable
      var props = initProperties;
      initProperties = null;

      var concatenatedProperties = this.concatenatedProperties;

      for (var i = 0, l = props.length; i < l; i++) {
        var properties = props[i];

        Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Mixin));

        if (typeof properties !== 'object' && properties !== undefined) {
          throw new EmberError("Ember.Object.create only accepts objects.");
        }

        if (!properties) { continue; }

        var keyNames = keys(properties);

        for (var j = 0, ll = keyNames.length; j < ll; j++) {
          var keyName = keyNames[j];
          var value = properties[keyName];

          if (IS_BINDING.test(keyName)) {
            var bindings = m.bindings;
            if (!bindings) {
              bindings = m.bindings = {};
            } else if (!m.hasOwnProperty('bindings')) {
              bindings = m.bindings = o_create(m.bindings);
            }
            bindings[keyName] = value;
          }

          var desc = m.descs[keyName];

          Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof ComputedProperty));
          Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
          Ember.assert("`actions` must be provided at extend time, not at create " +
                       "time, when Ember.ActionHandler is used (i.e. views, " +
                       "controllers & routes).", !((keyName === 'actions') && ActionHandler.detect(this)));

          if (concatenatedProperties &&
              concatenatedProperties.length > 0 &&
              indexOf(concatenatedProperties, keyName) >= 0) {
            var baseValue = this[keyName];

            if (baseValue) {
              if ('function' === typeof baseValue.concat) {
                value = baseValue.concat(value);
              } else {
                value = makeArray(baseValue).concat(value);
              }
            } else {
              value = makeArray(value);
            }
          }

          if (desc) {
            desc.set(this, keyName, value);
          } else {
            if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
              this.setUnknownProperty(keyName, value);
            } else {
              if (Ember.FEATURES.isEnabled('mandatory-setter')) {
                if (hasPropertyAccessors) {
                  defineProperty(this, keyName, null, value); // setup mandatory setter
                } else {
                  this[keyName] = value;
                }
              } else {
                this[keyName] = value;
              }
            }
          }
        }
      }
    }

    finishPartial(this, m);

    var length = arguments.length;

    if (length === 0) {
      this.init();
    } else if (length === 1) {
      this.init(arguments[0]);
    } else {
      this.init.apply(this, arguments);
    }

    m.proto = proto;
    finishChains(this);
    sendEvent(this, 'init');
  };
Example #24
0
  propertiesFromHTMLOptions: function(options) {
    var hash    = options.hash;
    var data    = options.data;
    var classes = hash['class'];

    var extensions = {
      helperName: options.helperName || ''
    };

    if (hash.id) {
      extensions.elementId = hash.id;
    }

    if (hash.tag) {
      extensions.tagName = hash.tag;
    }

    if (classes) {
      classes = classes.split(' ');
      extensions.classNames = classes;
    }

    if (hash.classBinding) {
      extensions.classNameBindings = hash.classBinding.split(' ');
    }

    if (hash.classNameBindings) {
      if (extensions.classNameBindings === undefined) {
        extensions.classNameBindings = [];
      }
      extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' '));
    }

    if (hash.attributeBindings) {
      Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed." +
                   " Please subclass Ember.View and set it there instead.");
      extensions.attributeBindings = null;
    }

    // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings
    // as well as class name bindings. If the bindings are local, make them relative to the current context
    // instead of the view.
    var path;
    var hashKeys = keys(hash);

    for (var i = 0, l = hashKeys.length; i < l; i++) {
      var prop      = hashKeys[i];
      var isBinding = IS_BINDING.test(prop);

      if (prop !== 'classNameBindings') {
        extensions[prop] = hash[prop];
      }

      // Test if the property ends in "Binding"
      if (isBinding && typeof extensions[prop] === 'string') {
        path = this.contextualizeBindingPath(hash[prop], data);
        if (path) {
          extensions[prop] = path;
        }
      }
    }

    if (extensions.classNameBindings) {
      // Evaluate the context of class name bindings:
      for (var j = 0, k = extensions.classNameBindings.length; j < k; j++) {
        var full = extensions.classNameBindings[j];

        if (typeof full === 'string') {
          // Contextualize the path of classNameBinding so this:
          //
          //     classNameBinding="isGreen:green"
          //
          // is converted to this:
          //
          //     classNameBinding="_parentView.context.isGreen:green"
          var parsedPath = View._parsePropertyPath(full);
          if (parsedPath.path !== '') {
            path = this.contextualizeBindingPath(parsedPath.path, data);
            if (path) {
              extensions.classNameBindings[j] = path + parsedPath.classNames;
            }
          }
        }
      }
    }

    return extensions;
  },
Example #25
0
    injections.push({
      property: property,
      fullName: normalizedInjectionName
    });
  },

  /**
   @method knownForType
   @param {String} type the type to iterate over
   @private
  */
  knownForType(type) {
    let fallbackKnown, resolverKnown;

    let localKnown = dictionary(null);
    let registeredNames = keys(this.registrations);
    for (let index = 0, length = registeredNames.length; index < length; index++) {
      let fullName = registeredNames[index];
      let itemType = fullName.split(':')[0];

      if (itemType === type) {
        localKnown[fullName] = true;
      }
    }

    if (this.fallback) {
      fallbackKnown = this.fallback.knownForType(type);
    }

    if (this.resolver.knownForType) {
      resolverKnown = this.resolver.knownForType(type);
Example #26
0
/**
  Namespace for injection helper methods.

  @class inject
  @namespace Ember
  */
function inject() {
  Ember.assert("Injected properties must be created through helpers, see `" +
               keys(inject).join("`, `") + "`");
}
Example #27
0
/**
  Strongly hint runtimes to intern the provided string.

  When do I need to use this function?

  For the most part, never. Pre-mature optimization is bad, and often the
  runtime does exactly what you need it to, and more often the trade-off isn't
  worth it.

  Why?

  Runtimes store strings in at least 2 different representations:
  Ropes and Symbols (interned strings). The Rope provides a memory efficient
  data-structure for strings created from concatenation or some other string
  manipulation like splitting.

  Unfortunately checking equality of different ropes can be quite costly as
  runtimes must resort to clever string comparison algorithims. These
  algorithims typically cost in proportion to the length of the string.
  Luckily, this is where the Symbols (interned strings) shine. As Symbols are
  unique by their string content, equality checks can be done by pointer
  comparision.

  How do I know if my string is a rope or symbol?

  Typically (warning general sweeping statement, but truthy in runtimes at
  present) static strings created as part of the JS source are interned.
  Strings often used for comparisions can be interned at runtime if some
  criteria are met.  One of these criteria can be the size of the entire rope.
  For example, in chrome 38 a rope longer then 12 characters will not
  intern, nor will segments of that rope.

  Some numbers: http://jsperf.com/eval-vs-keys/8

  Known Trick™

  @private
  @return {String} interned version of the provided string
*/
function intern(string) {
  var obj = Object.create(null);
  obj[string] = true;
  return keys(obj)[0];
}
Example #28
0
  propertiesFromHTMLOptions: function(hash, options, env) {
    var view    = env.data.view;
    var classes = read(hash['class']);

    var extensions = {
      helperName: options.helperName || ''
    };

    if (hash.id) {
      extensions.elementId = read(hash.id);
    }

    if (hash.tag) {
      extensions.tagName = hash.tag;
    }

    if (classes) {
      classes = classes.split(' ');
      extensions.classNames = classes;
    }

    if (hash.classBinding) {
      extensions.classNameBindings = hash.classBinding.split(' ');
    }

    if (hash.classNameBindings) {
      if (extensions.classNameBindings === undefined) {
        extensions.classNameBindings = [];
      }
      extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' '));
    }

    if (hash.attributeBindings) {
      Ember.assert("Setting 'attributeBindings' via template helpers is not allowed." +
                   " Please subclass Ember.View and set it there instead.");
      extensions.attributeBindings = null;
    }

    // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings
    // as well as class name bindings. If the bindings are local, make them relative to the current context
    // instead of the view.

    var hashKeys = keys(hash);

    for (var i = 0, l = hashKeys.length; i < l; i++) {
      var prop = hashKeys[i];

      if (prop !== 'classNameBindings') {
        extensions[prop] = hash[prop];
      }
    }

    var classNameBindings = extensions.classNameBindings;
    if (classNameBindings) {
      for (var j = 0; j < classNameBindings.length; j++) {
        var parsedPath = View._parsePropertyPath(classNameBindings[j]);
        if (parsedPath.path === '') {
          parsedPath.stream = new SimpleStream(true);
        } else {
          parsedPath.stream = view.getStream(parsedPath.path);
        }
        classNameBindings[j] = parsedPath;
      }
    }

    return extensions;
  },
Example #29
0
    var controller = scope.view ? null : read(scope.self);

    return {
      manager: state.manager,
      parentView: scope.view,
      controller,
      targetObject,
      viewClassOrInstance
    };
  },

  rerender(morph, env, scope, params, hash, template, inverse, visitor) {
    // If the hash is empty, the component cannot have extracted a part
    // of a mutable param and used it in its layout, because there are
    // no params at all.
    if (objectKeys(hash).length) {
      return morph.state.manager.rerender(env, hash, visitor, true);
    }
  },

  render(node, env, scope, params, hash, template, inverse, visitor) {
    if (hash.tag) {
      hash = swapKey(hash, 'tag', 'tagName');
    }

    if (hash.classNameBindings) {
      hash.classNameBindings = hash.classNameBindings.split(' ');
    }

    var state = node.state;
    var parentView = state.parentView;
Example #30
0
  var Class = function() {
    if (!wasApplied) {
      Class.proto(); // prepare prototype...
    }
    this.__defineNonEnumerable(GUID_KEY_PROPERTY);
    this.__defineNonEnumerable(NEXT_SUPER_PROPERTY);
    var m = meta(this);
    var proto = m.proto;
    m.proto = this;
    if (initMixins) {
      // capture locally so we can clear the closed over variable
      var mixins = initMixins;
      initMixins = null;
      apply(this, this.reopen, mixins);
    }
    if (initProperties) {
      // capture locally so we can clear the closed over variable
      var props = initProperties;
      initProperties = null;

      var concatenatedProperties = this.concatenatedProperties;
      var mergedProperties = this.mergedProperties;

      for (var i = 0, l = props.length; i < l; i++) {
        var properties = props[i];

        Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Mixin));

        if (typeof properties !== 'object' && properties !== undefined) {
          throw new EmberError("Ember.Object.create only accepts objects.");
        }

        if (!properties) { continue; }

        var keyNames = keys(properties);

        for (var j = 0, ll = keyNames.length; j < ll; j++) {
          var keyName = keyNames[j];
          var value = properties[keyName];

          if (IS_BINDING.test(keyName)) {
            var bindings = m.bindings;
            if (!bindings) {
              bindings = m.bindings = {};
            } else if (!m.hasOwnProperty('bindings')) {
              bindings = m.bindings = o_create(m.bindings);
            }
            bindings[keyName] = value;
          }

          var possibleDesc = this[keyName];
          var desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined;

          Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof ComputedProperty));
          Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
          Ember.assert("`actions` must be provided at extend time, not at create " +
                       "time, when Ember.ActionHandler is used (i.e. views, " +
                       "controllers & routes).", !((keyName === 'actions') && ActionHandler.detect(this)));

          if (concatenatedProperties &&
              concatenatedProperties.length > 0 &&
              indexOf(concatenatedProperties, keyName) >= 0) {
            var baseValue = this[keyName];

            if (baseValue) {
              if ('function' === typeof baseValue.concat) {
                value = baseValue.concat(value);
              } else {
                value = makeArray(baseValue).concat(value);
              }
            } else {
              value = makeArray(value);
            }
          }

          if (mergedProperties &&
              mergedProperties.length &&
              indexOf(mergedProperties, keyName) >= 0) {
            var originalValue = this[keyName];

            value = merge(originalValue, value);
          }

          if (desc) {
            desc.set(this, keyName, value);
          } else {
            if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
              this.setUnknownProperty(keyName, value);
            } else {
              if (isEnabled('mandatory-setter')) {
                if (hasPropertyAccessors) {
                  defineProperty(this, keyName, null, value); // setup mandatory setter
                } else {
                  this[keyName] = value;
                }
              } else {
                this[keyName] = value;
              }
            }
          }
        }
      }
    }

    finishPartial(this, m);

    var length = arguments.length;

    if (length === 0) {
      this.init();
    } else if (length === 1) {
      this.init(arguments[0]);
    } else {
      // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709
      // we may want to keep this around till this ages out on mobile
      var args = new Array(length);
      for (var x = 0; x < length; x++) {
        args[x] = arguments[x];
      }
      this.init.apply(this, args);
    }

    m.proto = proto;
    finishChains(this);
    sendEvent(this, 'init');
  };