UNPKG

todomvc

Version:

> Helping you select an MV\* framework

591 lines (555 loc) 18.8 kB
/*! * CanJS - 2.0.3 * http://canjs.us/ * Copyright (c) 2013 Bitovi * Tue, 26 Nov 2013 18:21:22 GMT * Licensed MIT * Includes: CanJS default build * Download from: http://canjs.us/ */ define(["can/util/string"], function(can) { // ## construct.js // `can.Construct` // _This is a modified version of // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/). // It provides class level inheritance and callbacks._ // A private flag used to initialize a new class instance without // initializing it's bindings. var initializing = 0; /** * @add can.Construct */ can.Construct = function() { if (arguments.length) { return can.Construct.extend.apply(can.Construct, arguments); } }; /** * @static */ can.extend(can.Construct, { /** * @property {Boolean} can.Construct.constructorExtends constructorExtends * @parent can.Construct.static * * @description * * Toggles the behavior of a constructor function called * without `new` to extend the constructor function or * create a new instance. * * @body * * If `constructorExtends` is: * * - `true` - the constructor extends * - `false` - a new instance of the constructor is created * * For 1.1, `constructorExtends` defaults to true. For * 1.2, `constructorExtends` will default to false. */ constructorExtends: true, /** * @function can.Construct.newInstance newInstance * @parent can.Construct.static * * @description Returns an instance of `can.Construct`. This method * can be overridden to return a cached instance. * * @signature `can.Construct.newInstance([...args])` * * @param {*} [args] arguments that get passed to [can.Construct::setup] and [can.Construct::init]. Note * that if [can.Construct::setup] returns an array, those arguments will be passed to [can.Construct::init] * instead. * @return {class} instance of the class * * @body * Creates a new instance of the constructor function. This method is useful for creating new instances * with arbitrary parameters. Typically, however, you will simply want to call the constructor with the * __new__ operator. * * ## Example * * The following creates a `Person` Construct and then creates a new instance of Person, * using `apply` on newInstance to pass arbitrary parameters. * * @codestart * var Person = can.Construct.extend({ * init : function(first, middle, last) { * this.first = first; * this.middle = middle; * this.last = last; * } * }); * * var args = ["Justin","Barry","Meyer"], * justin = new Person.newInstance.apply(null, args); * @codeend */ newInstance: function() { // Get a raw instance object (`init` is not called). var inst = this.instance(), arg = arguments, args; // Call `setup` if there is a `setup` if ( inst.setup ) { args = inst.setup.apply(inst, arguments); } // Call `init` if there is an `init` // If `setup` returned `args`, use those as the arguments if ( inst.init ) { inst.init.apply(inst, args || arguments); } return inst; }, // Overwrites an object with methods. Used in the `super` plugin. // `newProps` - New properties to add. // `oldProps` - Where the old properties might be (used with `super`). // `addTo` - What we are adding to. _inherit: function( newProps, oldProps, addTo ) { can.extend(addTo || newProps, newProps || {}) }, // used for overwriting a single property. // this should be used for patching other objects // the super plugin overwrites this _overwrite : function(what, oldProps, propName, val){ what[propName] = val; }, // Set `defaults` as the merger of the parent `defaults` and this // object's `defaults`. If you overwrite this method, make sure to // include option merging logic. /** * @function can.Construct.setup setup * @parent can.Construct.static * * @description Perform initialization logic for a constructor function. * * @signature `can.Construct.setup(base, fullName, staticProps, protoProps)` * * A static `setup` method provides inheritable setup functionality * for a Constructor function. The following example * creates a Group constructor function. Any constructor * functions that inherit from Group will be added to * `Group.childGroups`. * * * Group = can.Construct.extend({ * setup: function(Construct, fullName, staticProps, protoProps){ * this.childGroups = []; * if(Construct !== can.Construct){ * this.childGroups(Construct) * } * Construct.setup.apply(this, arguments) * } * },{}) * var Flock = Group.extend(...) * Group.childGroups[0] //-> Flock * * @param {constructor} base The base constructor that is being inherited from. * @param {String} fullName The name of the new constructor. * @param {Object} staticProps The static properties of the new constructor. * @param {Object} protoProps The prototype properties of the new constructor. * * @body * The static `setup` method is called immediately after a constructor * function is created and * set to inherit from its base constructor. It is useful for setting up * additional inheritance work. * Do not confuse this with the prototype `[can.Construct::setup]` method. * * ## Setup Extends Defaults * * Setup deeply extends the static `defaults` property of the base constructor with * properties of the inheriting constructor. For example: * * @codestart * Parent = can.Construct.extend({ * defaults : { * parentProp: 'foo' * } * },{}) * * Child = Parent.extend({ * defaults : { * childProp : 'bar' * } * },{} * * Child.defaults // {parentProp: 'foo', 'childProp': 'bar'} * @codeend * * ## Example * * This `Parent` class adds a reference to its base class to itself, and * so do all the classes that inherit from it. * * @codestart * Parent = can.Construct.extend({ * setup : function(base, fullName, staticProps, protoProps){ * this.base = base; * * // call base functionality * can.Construct.setup.apply(this, arguments) * } * },{}); * * Parent.base; // can.Construct * * Child = Parent({}); * * Child.base; // Parent * @codeend */ setup: function( base, fullName ) { this.defaults = can.extend(true,{}, base.defaults, this.defaults); }, // Create's a new `class` instance without initializing by setting the // `initializing` flag. instance: function() { // Prevents running `init`. initializing = 1; var inst = new this(); // Allow running `init`. initializing = 0; return inst; }, // Extends classes. /** * @function can.Construct.extend extend * @parent can.Construct.static * * @signature `can.Construct.extend([name,] [staticProperties,] instanceProperties)` * * Extends `can.Construct`, or constructor functions derived from `can.Construct`, * to create a new constructor function. Example: * * Animal = can.Construct.extend({ * sayHi: function(){ * console.log("hi") * } * }) * var animal = new Animal() * animal.sayHi(); * * @param {String} [name] Creates the necessary properties and * objects that point from the `window` to the created constructor function. The following: * * can.Construct.extend("company.project.Constructor",{}) * * creates a `company` object on window if it does not find one, a * `project` object on `company` if it does not find one, and it will set the * `Constructor` property on the `project` object to point to the constructor function. * * Finally, it sets "company.project.Constructor" as [can.Construct.fullName fullName] * and "Constructor" as [can.Construct.shortName shortName]. * * @param {Object} [staticProperties] Properties that are added the constructor * function directly. For example: * * Animal = can.Construct.extend({ * findAll: function(){ * return can.ajax({url: "/animals"}) * } * },{}); * * Animal.findAll().then(function(json){ ... }) * * The [can.Construct.setup static setup] method can be used to * specify inheritable behavior when a Constructor function is created. * * @param {Object} instanceProperties Properties that belong to * instances made with the constructor. These properties are added to the * constructor's `prototype` object. Example: * * Animal = can.Construct.extend({ * init: function(name){ * this.name = name; * }, * sayHi: function(){ * console.log(this.name,"says hi") * } * }) * var animal = new Animal() * animal.sayHi(); * * The [can.Construct::init init] and [can.Construct::setup setup] properties * are used for initialization. * * @return {function} The constructor function. * */ extend: function( fullName, klass, proto ) { // Figure out what was passed and normalize it. if ( typeof fullName != 'string' ) { proto = klass; klass = fullName; fullName = null; } if ( ! proto ) { proto = klass; klass = null; } proto = proto || {}; var _super_class = this, _super = this.prototype, name, shortName, namespace, prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor). prototype = this.instance(); // Copy the properties over onto the new prototype. can.Construct._inherit(proto, _super, prototype); // The dummy class constructor. function Constructor() { // All construction is actually done in the init method. if ( ! initializing ) { return this.constructor !== Constructor && arguments.length && Constructor.constructorExtends? // We are being called without `new` or we are extending. arguments.callee.extend.apply(arguments.callee, arguments) : // We are being called with `new`. Constructor.newInstance.apply(Constructor, arguments); } } // Copy old stuff onto class (can probably be merged w/ inherit) for ( name in _super_class ) { if ( _super_class.hasOwnProperty(name) ) { Constructor[name] = _super_class[name]; } } // Copy new static properties on class. can.Construct._inherit(klass, _super_class, Constructor); // Setup namespaces. if ( fullName ) { var parts = fullName.split('.'), shortName = parts.pop(), current = can.getObject(parts.join('.'), window, true), namespace = current, _fullName = can.underscore(fullName.replace(/\./g, "_")), _shortName = can.underscore(shortName); current[shortName] = Constructor; } // Set things that shouldn't be overwritten. can.extend(Constructor, { constructor: Constructor, prototype: prototype, /** * @property {String} can.Construct.namespace namespace * @parent can.Construct.static * * The `namespace` property returns the namespace your constructor is in. * This provides a way organize code and ensure globally unique types. The * `namespace` is the [can.Construct.fullName fullName] you passed without the [can.Construct.shortName shortName]. * * @codestart * can.Construct("MyApplication.MyConstructor",{},{}); * MyApplication.MyConstructor.namespace // "MyApplication" * MyApplication.MyConstructor.shortName // "MyConstructor" * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" * @codeend */ namespace: namespace, /** * @property {String} can.Construct.shortName shortName * @parent can.Construct.static * * If you pass a name when creating a Construct, the `shortName` property will be set to the * name you passed without the [can.Construct.namespace namespace]. * * @codestart * can.Construct("MyApplication.MyConstructor",{},{}); * MyApplication.MyConstructor.namespace // "MyApplication" * MyApplication.MyConstructor.shortName // "MyConstructor" * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" * @codeend */ _shortName : _shortName, /** * @property {String} can.Construct.fullName fullName * @parent can.Construct.static * * If you pass a name when creating a Construct, the `fullName` property will be set to * the name you passed. The `fullName` consists of the [can.Construct.namespace namespace] and * the [can.Construct.shortName shortName]. * * @codestart * can.Construct("MyApplication.MyConstructor",{},{}); * MyApplication.MyConstructor.namespace // "MyApplication" * MyApplication.MyConstructor.shortName // "MyConstructor" * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" * @codeend */ fullName: fullName, _fullName: _fullName }); // Dojo and YUI extend undefined if(shortName !== undefined) { Constructor.shortName = shortName; } // Make sure our prototype looks nice. Constructor.prototype.constructor = Constructor; // Call the class `setup` and `init` var t = [_super_class].concat(can.makeArray(arguments)), args = Constructor.setup.apply(Constructor, t ); if ( Constructor.init ) { Constructor.init.apply(Constructor, args || t ); } /** * @prototype */ return Constructor; // /** * @property {Object} can.Construct.prototype.constructor constructor * @parent can.Construct.prototype * * A reference to the constructor function that created the instance. This allows you to access * the constructor's static properties from an instance. * * ## Example * * This can.Construct has a static counter that counts how many instances have been created: * * @codestart * can.Construct.extend("Counter", { * count: 0 * }, { * init: function() { * this.constructor.count++; * } * }); * * new Counter(); * Counter.count; // 1 * @codeend */ } }); /** * @function can.Construct.prototype.setup setup * @parent can.Construct.prototype * * @signature `construct.setup(...args)` * * A setup function for the instantiation of a constructor function. * * @param {*} args The arguments passed to the constructor. * * @return {Array|undefined} If an array is returned, the array's items are passed as * arguments to [can.Construct::init init]. The following example always makes * sure that init is called with a jQuery wrapped element: * * WidgetFactory = can.Construct.extend({ * setup: function(element){ * return [$(element)] * } * }) * * MyWidget = WidgetFactory.extend({ * init: function($el){ * $el.html("My Widget!!") * } * }) * * Otherwise, the arguments to the * constructor are passed to [can.Construct::init] and the return value of `setup` is discarded. * * @body * * ## Deciding between `setup` and `init` * * * Usually, you should use [can.Construct::init init] to do your constructor function's initialization. * Use `setup` instead for: * * - initialization code that you want to run before the inheriting constructor's * `init` method is called. * - initialization code that should run whether or not inheriting constructors * call their base's `init` methods. * - modifying the arguments that will get passed to `init`. * * ## Example * * This code is a simplified version of the code in [can.Control]'s setup * method. It converts the first argument to a jQuery collection and * extends the controller's defaults with the options that were passed. * * * can.Control = can.Construct.extend({ * setup: function(domElement, rawOptions) { * // set up this.element * this.element = $(domElement); * * // set up this.options * this.options = can.extend({}, * this.constructor.defaults, * rawOptions * ); * * // pass this.element and this.options to init. * return [this.element, this.options]; * } * }); * */ can.Construct.prototype.setup = function(){}; /** * @function can.Construct.prototype.init init * @parent can.Construct.prototype * * @description Called when a new instance of a can.Construct is created. * * @signature `construct.init(...args)` * @param {*} args the arguments passed to the constructor (or the items of the array returned from [can.Construct::setup]) * * @body * If a prototype `init` method is provided, it is called when a new Construct is created, * after [can.Construct::setup]. The `init` method is where the bulk of your initialization code * should go, and a common thing to do in `init` is to save the arguments passed into the constructor. * * ## Examples * * First, we'll make a Person constructor that has a first and last name: * * @codestart * var Person = can.Construct.extend({ * init: function(first, last) { * this.first = first; * this.last = last; * } * }); * * var justin = new Person("Justin", "Meyer"); * justin.first; // "Justin" * justin.last; // "Meyer" * @codeend * * Then we'll extend Person into Programmer and add a favorite language: * * @codestart * var Programmer = Person.extend({ * init: function(first, last, language) { * // call base's init * Person.prototype.init.apply(this, arguments); * * // other initialization code * this.language = language; * }, * bio: function() { * return "Hi! I'm "" + this.first + " " + this.last + * " and I write " + this.language + "."; * } * }); * * var brian = new Programmer("Brian", "Moschel", 'ECMAScript'); * brian.bio(); // "Hi! I'm Brian Moschel and I write ECMAScript."; * @codeend * * ## Modified Arguments * * [can.Construct::setup] is able to modify the arguments passed to `init`. * If you aren't receiving the exact arguments as those passed to `new Construct(args)`, * check to make sure that they aren't being changed by `setup` somewhere along * the inheritance chain. */ can.Construct.prototype.init = function(){}; return can.Construct; });