/** * @author Ed Spencer * * The Store class encapsulates a client side cache of {@link Ext.data.Model Model} objects. Stores load * data via a {@link Ext.data.proxy.Proxy Proxy}, and also provide functions for {@link #sort sorting}, * {@link #filter filtering} and querying the {@link Ext.data.Model model} instances contained within it. * * Creating a Store is easy - we just tell it the Model and the Proxy to use to load and save its data: * * // Set up a {@link Ext.data.Model model} to use in our Store * Ext.define("User", { * extend: "Ext.data.Model", * config: { * fields: [ * {name: "firstName", type: "string"}, * {name: "lastName", type: "string"}, * {name: "age", type: "int"}, * {name: "eyeColor", type: "string"} * ] * } * }); * * var myStore = Ext.create("Ext.data.Store", { * model: "User", * proxy: { * type: "ajax", * url : "/users.json", * reader: { * type: "json", * rootProperty: "users" * } * }, * autoLoad: true * }); * * Ext.create("Ext.List", { * fullscreen: true, * store: myStore, * itemTpl: "{lastName}, {firstName} ({age})" * }); * * In the example above we configured an AJAX proxy to load data from the url '/users.json'. We told our Proxy * to use a {@link Ext.data.reader.Json JsonReader} to parse the response from the server into Model object - * {@link Ext.data.reader.Json see the docs on JsonReader} for details. * * The external data file, _/users.json_, is as follows: * * { * "success": true, * "users": [ * { * "firstName": "Tommy", * "lastName": "Maintz", * "age": 24, * "eyeColor": "green" * }, * { * "firstName": "Aaron", * "lastName": "Conran", * "age": 26, * "eyeColor": "blue" * }, * { * "firstName": "Jamie", * "lastName": "Avins", * "age": 37, * "eyeColor": "brown" * } * ] * } * * ## Inline data * * Stores can also load data inline. Internally, Store converts each of the objects we pass in as {@link #cfg-data} * into Model instances: * * @example * // Set up a model to use in our Store * Ext.define('User', { * extend: 'Ext.data.Model', * config: { * fields: [ * {name: 'firstName', type: 'string'}, * {name: 'lastName', type: 'string'}, * {name: 'age', type: 'int'}, * {name: 'eyeColor', type: 'string'} * ] * } * }); * * Ext.create("Ext.data.Store", { * storeId: "usersStore", * model: "User", * data : [ * {firstName: "Ed", lastName: "Spencer"}, * {firstName: "Tommy", lastName: "Maintz"}, * {firstName: "Aaron", lastName: "Conran"}, * {firstName: "Jamie", lastName: "Avins"} * ] * }); * * Ext.create("Ext.List", { * fullscreen: true, * store: "usersStore", * itemTpl: "{lastName}, {firstName}" * }); * * Loading inline data using the method above is great if the data is in the correct format already (e.g. it doesn't need * to be processed by a {@link Ext.data.reader.Reader reader}). If your inline data requires processing to decode the data structure, * use a {@link Ext.data.proxy.Memory MemoryProxy} instead (see the {@link Ext.data.proxy.Memory MemoryProxy} docs for an example). * * Additional data can also be loaded locally using {@link #method-add}. * * ## Loading Nested Data * * Applications often need to load sets of associated data - for example a CRM system might load a User and her Orders. * Instead of issuing an AJAX request for the User and a series of additional AJAX requests for each Order, we can load a nested dataset * and allow the Reader to automatically populate the associated models. Below is a brief example, see the {@link Ext.data.reader.Reader} intro * documentation for a full explanation: * * // Set up a model to use in our Store * Ext.define('User', { * extend: 'Ext.data.Model', * config: { * fields: [ * {name: 'name', type: 'string'}, * {name: 'id', type: 'int'} * ] * } * }); * * var store = Ext.create('Ext.data.Store', { * autoLoad: true, * model: "User", * proxy: { * type: 'ajax', * url : 'users.json', * reader: { * type: 'json', * rootProperty: 'users' * } * } * }); * * Ext.create("Ext.List", { * fullscreen: true, * store: store, * itemTpl: "{name} (id: {id})" * }); * * Which would consume a response like this: * * { * "users": [ * { * "id": 1, * "name": "Ed", * "orders": [ * { * "id": 10, * "total": 10.76, * "status": "invoiced" * }, * { * "id": 11, * "total": 13.45, * "status": "shipped" * } * ] * }, * { * "id": 3, * "name": "Tommy", * "orders": [ * ] * }, * { * "id": 4, * "name": "Jamie", * "orders": [ * { * "id": 12, * "total": 17.76, * "status": "shipped" * } * ] * } * ] * } * * See the {@link Ext.data.reader.Reader} intro docs for a full explanation. * * ## Filtering and Sorting * * Stores can be sorted and filtered - in both cases either remotely or locally. The {@link #sorters} and {@link #filters} are * held inside {@link Ext.util.MixedCollection MixedCollection} instances to make them easy to manage. Usually it is sufficient to * either just specify sorters and filters in the Store configuration or call {@link #sort} or {@link #filter}: * * // Set up a model to use in our Store * Ext.define('User', { * extend: 'Ext.data.Model', * config: { * fields: [ * {name: 'firstName', type: 'string'}, * {name: 'lastName', type: 'string'}, * {name: 'age', type: 'int'} * ] * } * }); * * var store = Ext.create("Ext.data.Store", { * autoLoad: true, * model: "User", * proxy: { * type: "ajax", * url : "users.json", * reader: { * type: "json", * rootProperty: "users" * } * }, * sorters: [ * { * property : "age", * direction: "DESC" * }, * { * property : "firstName", * direction: "ASC" * } * ], * filters: [ * { * property: "firstName", * value: /Jamie/ * } * ] * }); * * Ext.create("Ext.List", { * fullscreen: true, * store: store, * itemTpl: "{lastName}, {firstName} ({age})" * }); * * And the data file, _users.json_, is as follows: * * { * "success": true, * "users": [ * { * "firstName": "Tommy", * "lastName": "Maintz", * "age": 24 * }, * { * "firstName": "Aaron", * "lastName": "Conran", * "age": 26 * }, * { * "firstName": "Jamie", * "lastName": "Avins", * "age": 37 * } * ] * } * * The new Store will keep the configured sorters and filters in the MixedCollection instances mentioned above. By default, sorting * and filtering are both performed locally by the Store - see {@link #remoteSort} and {@link #remoteFilter} to allow the server to * perform these operations instead. * * Filtering and sorting after the Store has been instantiated is also easy. Calling {@link #filter} adds another filter to the Store * and automatically filters the dataset (calling {@link #filter} with no arguments simply re-applies all existing filters). Note that by * default your sorters are automatically reapplied if using local sorting. * * store.filter('eyeColor', 'Brown'); * * Change the sorting at any time by calling {@link #sort}: * * store.sort('height', 'ASC'); * * Note that all existing sorters will be removed in favor of the new sorter data (if {@link #sort} is called with no arguments, * the existing sorters are just reapplied instead of being removed). To keep existing sorters and add new ones, just add them * to the MixedCollection: * * store.sorters.add(new Ext.util.Sorter({ * property : 'shoeSize', * direction: 'ASC' * })); * * store.sort(); * * ## Registering with StoreManager * * Any Store that is instantiated with a {@link #storeId} will automatically be registered with the {@link Ext.data.StoreManager StoreManager}. * This makes it easy to reuse the same store in multiple views: * * // this store can be used several times * Ext.create('Ext.data.Store', { * model: 'User', * storeId: 'usersStore' * }); * * Ext.create('Ext.List', { * store: 'usersStore' * // other config goes here * }); * * Ext.create('Ext.view.View', { * store: 'usersStore' * // other config goes here * }); * * ## Further Reading * * Stores are backed up by an ecosystem of classes that enables their operation. To gain a full understanding of these * pieces and how they fit together, see: * * - {@link Ext.data.proxy.Proxy Proxy} - overview of what Proxies are and how they are used * - {@link Ext.data.Model Model} - the core class in the data package * - {@link Ext.data.reader.Reader Reader} - used by any subclass of {@link Ext.data.proxy.Server ServerProxy} to read a response */ Ext.define('Ext.data.Store', { alias: 'store.store', extend: 'Ext.Evented', requires: [ 'Ext.util.Collection', 'Ext.data.Operation', 'Ext.data.proxy.Memory', 'Ext.data.Model', 'Ext.data.StoreManager', 'Ext.util.Grouper' ], /** * @event addrecords * Fired when one or more new Model instances have been added to this Store. You should listen * for this event if you have to update a representation of the records in this store in your UI. * If you need the indices of the records that were added please use the store.indexOf(record) method. * @param {Ext.data.Store} store The store * @param {Ext.data.Model[]} records The Model instances that were added */ /** * @event removerecords * Fired when one or more Model instances have been removed from this Store. You should listen * for this event if you have to update a representation of the records in this store in your UI. * @param {Ext.data.Store} store The Store object * @param {Ext.data.Model[]} records The Model instances that was removed * @param {Number[]} indices The indices of the records that were removed. These indices already * take into account any potential earlier records that you remove. This means that if you loop * over the records, you can get its current index in your data representation from this array. */ /** * @event updaterecord * Fires when a Model instance has been updated * @param {Ext.data.Store} this * @param {Ext.data.Model} record The Model instance that was updated * @param {Number} newIndex If the update changed the index of the record (due to sorting for example), then * this gives you the new index in the store. * @param {Number} oldIndex If the update changed the index of the record (due to sorting for example), then * this gives you the old index in the store. * @param {Array} modifiedFieldNames An array containing the field names that have been modified since the * record was committed or created * @param {Object} modifiedValues An object where each key represents a field name that had it's value modified, * and where the value represents the old value for that field. To get the new value in a listener * you should use the {@link Ext.data.Model#get get} method. */ /** * @event update * @inheritdoc Ext.data.Store#updaterecord * @removed 2.0 Listen to #updaterecord instead. */ /** * @event refresh * Fires whenever the records in the Store have changed in a way that your representation of the records * need to be entirely refreshed. * @param {Ext.data.Store} this The data store * @param {Ext.util.Collection} data The data collection containing all the records */ /** * @event beforeload * Fires before a request is made for a new data object. If the beforeload handler returns false the load * action will be canceled. Note that you should not listen for this event in order to refresh the * data view. Use the {@link #refresh} event for this instead. * @param {Ext.data.Store} store This Store * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to * load the Store */ /** * @event load * Fires whenever records have been loaded into the store. Note that you should not listen * for this event in order to refresh the data view. Use the {@link #refresh} event for this instead. * @param {Ext.data.Store} this * @param {Ext.data.Model[]} records An array of records * @param {Boolean} successful `true` if the operation was successful. * @param {Ext.data.Operation} operation The associated operation. */ /** * @event write * Fires whenever a successful write has been made via the configured {@link #proxy Proxy} * @param {Ext.data.Store} store This Store * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object that was used in * the write */ /** * @event beforesync * Fired before a call to {@link #sync} is executed. Return `false` from any listener to cancel the sync * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy */ /** * @event clear * Fired after the {@link #removeAll} method is called. Note that you should not listen for this event in order * to refresh the data view. Use the {@link #refresh} event for this instead. * @param {Ext.data.Store} this * @return {Ext.data.Store} */ statics: { create: function(store) { if (!store.isStore) { if (!store.type) { store.type = 'store'; } store = Ext.createByAlias('store.' + store.type, store); } return store; } }, isStore: true, config: { /** * @cfg {String} storeId * Unique identifier for this store. If present, this Store will be registered with the {@link Ext.data.StoreManager}, * making it easy to reuse elsewhere. * @accessor */ storeId: undefined, /** * @cfg {Object[]/Ext.data.Model[]} data * Array of Model instances or data objects to load locally. See "Inline data" above for details. * @accessor */ data: null, /** * @cfg {Boolean/Object} [autoLoad=false] * If data is not specified, and if `autoLoad` is `true` or an Object, this store's load method is automatically called * after creation. If the value of `autoLoad` is an Object, this Object will be passed to the store's `load()` method. * @accessor */ autoLoad: null, /** * @cfg {Boolean} autoSync * `true` to automatically sync the Store with its Proxy after every edit to one of its Records. * @accessor */ autoSync: false, /** * @cfg {String} model * Returns Ext.data.Model and not a String. * Name of the {@link Ext.data.Model Model} associated with this store. * The string is used as an argument for {@link Ext.ModelManager#getModel}. * @accessor */ model: undefined, /** * @cfg {String/Ext.data.proxy.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config * object or a Proxy instance - see {@link #setProxy} for details. * @accessor */ proxy: undefined, /** * @cfg {Object[]/Ext.util.Collection} fields * Returns Ext.util.Collection not just an Object. * Use in place of specifying a {@link #model} configuration. The fields should be a * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model} * with these fields. In general this configuration option should be avoided, it exists for the purposes of * backwards compatibility. For anything more complicated, such as specifying a particular id property or * associations, a {@link Ext.data.Model} should be defined and specified for the {@link #model} * config. * @return Ext.util.Collection * @accessor */ fields: null, /** * @cfg {Boolean} remoteSort * `true` to defer any sorting operation to the server. If `false`, sorting is done locally on the client. * * If this is set to `true`, you will have to manually call the {@link #method-load} method after you {@link #method-sort}, to retrieve the sorted * data from the server. * * {@link #buffered Buffered} stores automatically set this to `true`. Buffered stores contain an abitrary * subset of the full dataset which depends upon various configurations and which pages have been requested * for rendering. Such *sparse* datasets are ineligible for local sorting. * @accessor */ remoteSort: false, /** * @cfg {Boolean} remoteFilter * `true` to defer any filtering operation to the server. If `false`, filtering is done locally on the client. * * If this is set to `true`, you will have to manually call the {@link #method-load} method after you {@link #method-filter} to retrieve the filtered * data from the server. * * {@link #buffered Buffered} stores automatically set this to `true`. Buffered stores contain an abitrary * subset of the full dataset which depends upon various configurations and which pages have been requested * for rendering. Such *sparse* datasets are ineligible for local filtering. * @accessor */ remoteFilter: false, /** * @cfg {Boolean} remoteGroup * `true` to defer any grouping operation to the server. If `false`, grouping is done locally on the client. * * {@link #buffered Buffered} stores automatically set this to `true`. Buffered stores contain an abitrary * subset of the full dataset which depends upon various configurations and which pages have been requested * for rendering. Such *sparse* datasets are ineligible for local grouping. * @accessor */ remoteGroup: false, /** * @cfg {Object[]} filters * Array of {@link Ext.util.Filter Filters} for this store. This configuration is handled by the * {@link Ext.mixin.Filterable Filterable} mixin of the {@link Ext.util.Collection data} collection. * @accessor */ filters: null, /** * @cfg {Object[]} sorters * Array of {@link Ext.util.Sorter Sorters} for this store. This configuration is handled by the * {@link Ext.mixin.Sortable Sortable} mixin of the {@link Ext.util.Collection data} collection. * See also the {@link #sort} method. * @accessor */ sorters: null, /** * @cfg {Object} grouper * A configuration object for this Store's {@link Ext.util.Grouper grouper}. * * For example, to group a store's items by the first letter of the last name: * * Ext.define('People', { * extend: 'Ext.data.Store', * * config: { * fields: ['first_name', 'last_name'], * * grouper: { * groupFn: function(record) { * return record.get('last_name').substr(0, 1); * }, * sortProperty: 'last_name' * } * } * }); * * @accessor */ grouper: null, /** * @cfg {String} groupField * The (optional) field by which to group data in the store. Internally, grouping is very similar to sorting - the * groupField and {@link #groupDir} are injected as the first sorter (see {@link #sort}). Stores support a single * level of grouping, and groups can be fetched via the {@link #getGroups} method. * @accessor */ groupField: null, /** * @cfg {String} groupDir * The direction in which sorting should be applied when grouping. If you specify a grouper by using the {@link #groupField} * configuration, this will automatically default to "ASC" - the other supported value is "DESC" * @accessor */ groupDir: null, /** * @cfg {Function} getGroupString This function will be passed to the {@link #grouper} configuration as it's `groupFn`. * Note that this configuration is deprecated and grouper: `{groupFn: yourFunction}}` is preferred. * @deprecated * @accessor */ getGroupString: null, /** * @cfg {Number} pageSize * The number of records considered to form a 'page'. This is used to power the built-in * paging using the nextPage and previousPage functions. * * If this Store is {@link #buffered}, pages are loaded into a page cache before the Store's * data is updated from the cache. The pageSize is the number of rows loaded into the cache in one request. * This will not affect the rendering of a buffered grid, but a larger page size will mean fewer loads. * * In a buffered grid, scrolling is monitored, and the page cache is kept primed with data ahead of the * direction of scroll to provide rapid access to data when scrolling causes it to be required. Several pages * in advance may be requested depending on various parameters. * * It is recommended to tune the {@link #pageSize}, {@link #trailingBufferZone} and * {@link #leadingBufferZone} configurations based upon the conditions pertaining in your deployed application. */ pageSize: 25, /** * @cfg {Number} totalCount The total number of records in the full dataset, as indicated by a server. If the * server-side dataset contains 5000 records but only returns pages of 50 at a time, `totalCount` will be set to * 5000 and {@link #getCount} will return 50 */ totalCount: null, /** * @cfg {Boolean} clearOnPageLoad `true` to empty the store when loading another page via {@link #loadPage}, * {@link #nextPage} or {@link #previousPage}. Setting to `false` keeps existing records, allowing * large data sets to be loaded one page at a time but rendered all together. * @accessor */ clearOnPageLoad: true, /** * @cfg {Object} params Parameters to send into the proxy for any CRUD operations * * @accessor */ params: {}, modelDefaults: {}, /** * @cfg {Boolean} autoDestroy This is a private configuration used in the framework whether this Store * can be destroyed. * @private */ autoDestroy: false, /** * @cfg {Boolean} syncRemovedRecords This configuration allows you to disable the synchronization of * removed records on this Store. By default, when you call `removeAll()` or `remove()`, records will be added * to an internal removed array. When you then sync the Store, we send a destroy request for these records. * If you don't want this to happen, you can set this configuration to `false`. */ syncRemovedRecords: true, /** * @cfg {Boolean} destroyRemovedRecords This configuration allows you to prevent destroying record * instances when they are removed from this store and are not in any other store. */ destroyRemovedRecords: true, /** * @cfg {Boolean} buffered * Allows the Store to prefetch and cache in a **page cache**, pages of Records, and to then satisfy * loading requirements from this page cache. * * To use buffered Stores, initiate the process by loading the first page. The number of rows rendered are * determined automatically, and the range of pages needed to keep the cache primed for scrolling is * requested and cached. * Example: * * myStore.loadPage(1); // Load page 1 * * A {@link Ext.grid.plugin.BufferedRenderer BufferedRenderer} is instantiated which will monitor the scrolling in the grid, and * refresh the view's rows from the page cache as needed. It will also pull new data into the page * cache when scrolling of the view draws upon data near either end of the prefetched data. * * The margins which trigger view refreshing from the prefetched data are {@link Ext.grid.plugin.BufferedRenderer#numFromEdge}, * {@link Ext.grid.plugin.BufferedRenderer#leadingBufferZone} and {@link Ext.grid.plugin.BufferedRenderer#trailingBufferZone}. * * The margins which trigger loading more data into the page cache are, {@link #leadingBufferZone} and * {@link #trailingBufferZone}. * * By default, only 5 pages of data are cached in the page cache, with pages "scrolling" out of the buffer * as the view moves down through the dataset. * Setting this value to zero means that no pages are *ever* scrolled out of the page cache, and * that eventually the whole dataset may become present in the page cache. This is sometimes desirable * as long as datasets do not reach astronomical proportions. * * Selection state may be maintained across page boundaries by configuring the SelectionModel not to discard * records from its collection when those Records cycle out of the Store's primary collection. This is done * by configuring the SelectionModel like this: * * selModel: { * pruneRemoved: false * } * */ buffered: false, /** * @cfg {Object/Array} plugins * @accessor * An object or array of objects that will provide custom functionality for this component. The only * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component. * * When a component is created, if any plugins are available, the component will call the init method on each * plugin, passing a reference to itself. Each plugin can then call methods or respond to events on the * component as needed to provide its functionality. * * For examples of plugins, see Ext.plugin.PullRefresh and Ext.plugin.ListPaging * * ## Example code * * A plugin by alias: * * Ext.create('Ext.dataview.List', { * config: { * plugins: 'listpaging', * itemTpl: '<div class="item">{title}</div>', * store: 'Items' * } * }); * * Multiple plugins by alias: * * Ext.create('Ext.dataview.List', { * config: { * plugins: ['listpaging', 'pullrefresh'], * itemTpl: '<div class="item">{title}</div>', * store: 'Items' * } * }); * * Single plugin by class name with config options: * * Ext.create('Ext.dataview.List', { * config: { * plugins: { * xclass: 'Ext.plugin.ListPaging', // Reference plugin by class * autoPaging: true * }, * * itemTpl: '<div class="item">{title}</div>', * store: 'Items' * } * }); * * Multiple plugins by class name with config options: * * Ext.create('Ext.dataview.List', { * config: { * plugins: [ * { * xclass: 'Ext.plugin.PullRefresh', * pullRefreshText: 'Pull to refresh...' * }, * { * xclass: 'Ext.plugin.ListPaging', * autoPaging: true * } * ], * * itemTpl: '<div class="item">{title}</div>', * store: 'Items' * } * }); * */ plugins: null }, /** * @property {Number} currentPage * The page that the Store has most recently loaded (see {@link #loadPage}) */ currentPage: 1, constructor: function(config) { config = config || {}; this.data = this._data = this.createDataCollection(); this.data.setSortRoot('data'); this.data.setFilterRoot('data'); this.removed = []; if (config.id && !config.storeId) { config.storeId = config.id; delete config.id; } // <deprecated product=touch since=2.0> // <debug> if (config.hasOwnProperty('sortOnLoad')) { Ext.Logger.deprecate( '[Ext.data.Store] sortOnLoad is always activated in Sencha Touch 2 so your Store is always fully ' + 'sorted after loading. The only exception is if you are using remoteSort and change sorting after ' + 'the Store as loaded, in which case you need to call store.load() to fetch the sorted data from the server.' ); } if (config.hasOwnProperty('filterOnLoad')) { Ext.Logger.deprecate( '[Ext.data.Store] filterOnLoad is always activated in Sencha Touch 2 so your Store is always fully ' + 'sorted after loading. The only exception is if you are using remoteFilter and change filtering after ' + 'the Store as loaded, in which case you need to call store.load() to fetch the filtered data from the server.' ); } if (config.hasOwnProperty('sortOnFilter')) { Ext.Logger.deprecate( '[Ext.data.Store] sortOnFilter is deprecated and is always effectively true when sorting and filtering locally' ); } // </debug> // </deprecated> this.initConfig(config); this.callParent(arguments); }, applyPlugins: function(config) { var ln, i, configObj; if (!config) { return config; } config = [].concat(config); for (i = 0, ln = config.length; i < ln; i++) { configObj = config[i]; config[i] = Ext.factory(configObj, 'Ext.plugin.Plugin', null, 'plugin'); } return config; }, updatePlugins: function(newPlugins, oldPlugins) { var ln, i; if (newPlugins) { for (i = 0, ln = newPlugins.length; i < ln; i++) { newPlugins[i].init(this); } } if (oldPlugins) { for (i = 0, ln = oldPlugins.length; i < ln; i++) { Ext.destroy(oldPlugins[i]); } } }, /** * @private * @return {Ext.util.Collection} */ createDataCollection: function() { return new Ext.util.Collection(function(record) { return record.getId(); }); }, applyStoreId: function(storeId) { if (storeId === undefined || storeId === null) { storeId = this.getUniqueId(); } return storeId; }, updateStoreId: function(storeId, oldStoreId) { if (oldStoreId) { Ext.data.StoreManager.unregister(this); } if (storeId) { Ext.data.StoreManager.register(this); } }, applyModel: function(model) { if (typeof model == 'string') { var registeredModel = Ext.data.ModelManager.getModel(model); if (!registeredModel) { Ext.Logger.error('Model with name "' + model + '" does not exist.'); } model = registeredModel; } if (model && !model.prototype.isModel && Ext.isObject(model)) { model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model); } if (!model) { var fields = this.getFields(), data = this.config.data; if (!fields && data && data.length) { fields = Ext.Object.getKeys(data[0]); } if (fields) { model = Ext.define('Ext.data.Store.ImplicitModel-' + (this.getStoreId() || Ext.id()), { extend: 'Ext.data.Model', config: { fields: fields, useCache: false, proxy: this.getProxy() } }); this.implicitModel = true; } } if (!model && this.getProxy()) { model = this.getProxy().getModel(); } // <debug> if (!model) { Ext.Logger.warn('Unless you define your model through metadata, a store needs to have a model defined on either itself or on its proxy'); } // </debug> return model; }, updateModel: function(model) { var proxy = this.getProxy(); if (proxy && !proxy.getModel()) { proxy.setModel(model); } }, applyProxy: function(proxy, currentProxy) { proxy = Ext.factory(proxy, Ext.data.Proxy, currentProxy, 'proxy'); if (!proxy && this.getModel()) { proxy = this.getModel().getProxy(); } if (!proxy) { proxy = new Ext.data.proxy.Memory({ model: this.getModel() }); } if (proxy.isMemoryProxy) { this.setSyncRemovedRecords(false); } return proxy; }, updateProxy: function(proxy, oldProxy) { if (proxy) { if (!proxy.getModel()) { proxy.setModel(this.getModel()); } proxy.on('metachange', 'onMetaChange', this); } if (oldProxy) { proxy.un('metachange', 'onMetaChange', this); } }, /** * We are using applyData so that we can return nothing and prevent the `this.data` * property to be overridden. * @param {Array/Object} data */ applyData: function(data) { var me = this, proxy; if (data) { proxy = me.getProxy(); if (proxy instanceof Ext.data.proxy.Memory) { proxy.setData(data); me.load(); return; } else { // We make it silent because we don't want to fire a refresh event me.removeAll(true); // This means we have to fire a clear event though me.fireEvent('clear', me); // We don't want to fire addrecords event since we will be firing // a refresh event later which will already take care of updating // any views bound to this store me.suspendEvents(); me.add(data); me.resumeEvents(); // We set this to true so isAutoLoading to try me.dataLoaded = true; } } else { me.removeAll(true); // This means we have to fire a clear event though me.fireEvent('clear', me); } me.fireEvent('refresh', me, me.data); }, clearData: function() { this.setData(null); }, /** * Uses the configured {@link Ext.data.reader.Reader reader} to convert the data into records * and adds it to the Store. Use this when you have raw data that needs to pass trough converters, * mappings and other extra logic from the reader. * * If your data is already formated and ready for consumption, use {@link #add} method. * * @param {Object[]} data Array of data to load */ addData: function(data) { var reader = this.getProxy().getReader(), resultSet = reader.read(data), records = resultSet.getRecords(); this.add(records); }, updateAutoLoad: function(autoLoad) { var proxy = this.getProxy(); if (autoLoad && (proxy && !proxy.isMemoryProxy)) { this.load(Ext.isObject(autoLoad) ? autoLoad : null); } }, /** * Returns `true` if the Store is set to {@link #autoLoad} or is a type which loads upon instantiation. * @return {Boolean} */ isAutoLoading: function() { var proxy = this.getProxy(); return (this.getAutoLoad() || (proxy && proxy.isMemoryProxy) || this.dataLoaded); }, updateGroupField: function(groupField) { var grouper = this.getGrouper(); if (groupField) { if (!grouper) { this.setGrouper({ property: groupField, direction: this.getGroupDir() || 'ASC' }); } else { grouper.setProperty(groupField); } } else if (grouper) { this.setGrouper(null); } }, updateGroupDir: function(groupDir) { var grouper = this.getGrouper(); if (grouper) { grouper.setDirection(groupDir); } }, applyGetGroupString: function(getGroupStringFn) { var grouper = this.getGrouper(); if (getGroupStringFn) { // <debug> Ext.Logger.warn('Specifying getGroupString on a store has been deprecated. Please use grouper: {groupFn: yourFunction}'); // </debug> if (grouper) { grouper.setGroupFn(getGroupStringFn); } else { this.setGrouper({ groupFn: getGroupStringFn }); } } else if (grouper) { this.setGrouper(null); } }, applyGrouper: function(grouper) { if (typeof grouper == 'string') { grouper = { property: grouper }; } else if (typeof grouper == 'function') { grouper = { groupFn: grouper }; } grouper = Ext.factory(grouper, Ext.util.Grouper); return grouper; }, updateGrouper: function(grouper, oldGrouper) { var data = this.data; if (oldGrouper) { data.removeSorter(oldGrouper); if (!grouper) { data.getSorters().removeSorter('isGrouper'); } } if (grouper) { data.insertSorter(0, grouper); if (!oldGrouper) { data.getSorters().addSorter({ direction: 'DESC', property: 'isGrouper', transform: function(value) { return (value === true) ? 1 : -1; } }); } } this.fireEvent('refresh', this, data); }, /** * This method tells you if this store has a grouper defined on it. * @return {Boolean} `true` if this store has a grouper defined. */ isGrouped: function() { return !!this.getGrouper(); }, updateSorters: function(sorters) { var grouper = this.getGrouper(), data = this.data, autoSort = data.getAutoSort(); // While we remove/add sorters we don't want to automatically sort because we still need // to apply any field sortTypes as transforms on the Sorters after we have added them. data.setAutoSort(false); data.setSorters(sorters); if (grouper) { data.insertSorter(0, grouper); } this.updateSortTypes(); // Now we put back autoSort on the Collection to the value it had before. If it was // auto sorted, setting this back will cause it to sort right away. data.setAutoSort(autoSort); }, updateSortTypes: function() { var model = this.getModel(), fields = model && model.getFields(), data = this.data; // We loop over each sorter and set it's transform method to the every field's sortType. if (fields) { data.getSorters().each(function(sorter) { var property = sorter.getProperty(), field; if (!sorter.isGrouper && property && !sorter.getTransform()) { field = fields.get(property); if (field) { sorter.setTransform(field.getSortType()); } } }); } }, updateFilters: function(filters) { this.data.setFilters(filters); }, /** * Adds Model instance to the Store. This method accepts either: * * - An array of Model instances or Model configuration objects. * - Any number of Model instance or Model configuration object arguments. * * The new Model instances will be added at the end of the existing collection. * * Sample usage: * * myStore.add({some: 'data2'}, {some: 'other data2'}); * * Use {@link #addData} method instead if you have raw data that need to pass * through the data reader. * * @param {Ext.data.Model[]/Ext.data.Model...} model An array of Model instances * or Model configuration objects, or variable number of Model instance or config arguments. * @return {Ext.data.Model[]} The model instances that were added. */ add: function(records) { if (!Ext.isArray(records)) { records = Array.prototype.slice.call(arguments); } return this.insert(this.data.length, records); }, /** * Inserts Model instances into the Store at the given index and fires the {@link #add} event. * See also `{@link #add}`. * @param {Number} index The start index at which to insert the passed Records. * @param {Ext.data.Model[]} records An Array of Ext.data.Model objects to add to the cache. * @return {Object} */ insert: function(index, records) { if (!Ext.isArray(records)) { records = Array.prototype.slice.call(arguments, 1); } var me = this, sync = false, data = this.data, ln = records.length, Model = this.getModel(), modelDefaults = me.getModelDefaults(), added = false, i, record; records = records.slice(); for (i = 0; i < ln; i++) { record = records[i]; if (!record.isModel) { record = new Model(record); } // If we are adding a record that is already an instance which was still in the // removed array, then we remove it from the removed array else if (this.removed.indexOf(record) != -1) { Ext.Array.remove(this.removed, record); } record.set(modelDefaults); record.join(me); records[i] = record; // If this is a newly created record, then we might want to sync it later sync = sync || (record.phantom === true); } // Now we insert all these records in one go to the collection. Saves many function // calls to data.insert. Does however create two loops over the records we are adding. if (records.length === 1) { added = data.insert(index, records[0]); if (added) { added = [added]; } } else { added = data.insertAll(index, records); } if (added) { me.fireEvent('addrecords', me, added); } if (me.getAutoSync() && sync) { me.sync(); } return records; }, /** * Removes the given record from the Store, firing the `removerecords` event passing all the instances that are removed. * @param {Ext.data.Model/Ext.data.Model[]} records Model instance or array of instances to remove. */ remove: function (records) { if (records.isModel) { records = [records]; } var me = this, sync = false, i = 0, autoSync = this.getAutoSync(), syncRemovedRecords = me.getSyncRemovedRecords(), destroyRemovedRecords = this.getDestroyRemovedRecords(), ln = records.length, indices = [], removed = [], isPhantom, items = me.data.items, record, index; for (; i < ln; i++) { record = records[i]; if (me.data.contains(record)) { isPhantom = (record.phantom === true); index = items.indexOf(record); if (index !== -1) { removed.push(record); indices.push(index); } record.unjoin(me); me.data.remove(record); if (destroyRemovedRecords && !syncRemovedRecords && !record.stores.length) { record.destroy(); } else if (!isPhantom && syncRemovedRecords) { // don't push phantom records onto removed me.removed.push(record); } sync = sync || !isPhantom; } } me.fireEvent('removerecords', me, removed, indices); if (autoSync && sync) { me.sync(); } }, /** * Removes the model instance at the given index. * @param {Number} index The record index. */ removeAt: function(index) { var record = this.getAt(index); if (record) { this.remove(record); } }, /** * Remove all items from the store. * @param {Boolean} [silent] Prevent the `clear` event from being fired. */ removeAll: function(silent) { if (silent !== true && this.eventFiringSuspended !== true) { this.fireAction('clear', [this], 'doRemoveAll'); } else { this.doRemoveAll.call(this, true); } }, doRemoveAll: function (silent) { var me = this, destroyRemovedRecords = this.getDestroyRemovedRecords(), syncRemovedRecords = this.getSyncRemovedRecords(), records = me.data.all.slice(), ln = records.length, i, record; for (i = 0; i < ln; i++) { record = records[i]; record.unjoin(me); if (destroyRemovedRecords && !syncRemovedRecords && !record.stores.length) { record.destroy(); } else if (record.phantom !== true && syncRemovedRecords) { me.removed.push(record); } } me.data.clear(); if (silent !== true) { me.fireEvent('refresh', me, me.data); } if (me.getAutoSync()) { this.sync(); } }, /** * Calls the specified function for each of the {@link Ext.data.Model Records} in the cache. * * // Set up a model to use in our Store * Ext.define('User', { * extend: 'Ext.data.Model', * config: { * fields: [ * {name: 'firstName', type: 'string'}, * {name: 'lastName', type: 'string'} * ] * } * }); * * var store = Ext.create('Ext.data.Store', { * model: 'User', * data : [ * {firstName: 'Ed', lastName: 'Spencer'}, * {firstName: 'Tommy', lastName: 'Maintz'}, * {firstName: 'Aaron', lastName: 'Conran'}, * {firstName: 'Jamie', lastName: 'Avins'} * ] * }); * * store.each(function (item, index, length) { * console.log(item.get('firstName'), index); * }); * * @param {Function} fn The function to call. Returning `false` aborts and exits the iteration. * @param {Ext.data.Model} fn.item * @param {Number} fn.index * @param {Number} fn.length * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. * Defaults to the current {@link Ext.data.Model Record} in the iteration. */ each: function(fn, scope) { this.data.each(fn, scope); }, /** * Gets the number of cached records. Note that filtered records are not included in this count. * If using paging, this may not be the total size of the dataset. * @return {Number} The number of Records in the Store's cache. */ getCount: function() { return this.data.items.length || 0; }, /** * Gets the number of all cached records including the ones currently filtered. * If using paging, this may not be the total size of the dataset. * @return {Number} The number of all Records in the Store's cache. */ getAllCount: function () { return this.data.all.length || 0; }, /** * Get the Record at the specified index. * @param {Number} index The index of the Record to find. * @return {Ext.data.Model/undefined} The Record at the passed index. Returns `undefined` if not found. */ getAt: function(index) { return this.data.getAt(index); }, /** * Returns a range of Records between specified indices. Note that if the store is filtered, only filtered results * are returned. * @param {Number} [startIndex=0] (optional) The starting index. * @param {Number} [endIndex=-1] (optional) The ending index (defaults to the last Record in the Store). * @return {Ext.data.Model[]} An array of Records. */ getRange: function(start, end) { return this.data.getRange(start, end); }, /** * Get the Record with the specified id. * @param {String} id The id of the Record to find. * @return {Ext.data.Model/undefined} The Record with the passed id. Returns `undefined` if not found. */ getById: function(id) { return this.data.findBy(function(record) { return record.getId() == id; }); }, /** * Get the index within the cache of the passed Record. * @param {Ext.data.Model} record The Ext.data.Model object to find. * @return {Number} The index of the passed Record. Returns -1 if not found. */ indexOf: function(record) { return this.data.indexOf(record); }, /** * Get the index within the cache of the Record with the passed id. * @param {String} id The id of the Record to find. * @return {Number} The index of the Record. Returns -1 if not found. */ indexOfId: function(id) { return this.data.indexOfKey(id); }, /** * @private * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to. * @param {Ext.data.Model} record The model instance that was edited. * @param {String[]} modifiedFieldNames Array of field names changed during edit. * @param {Object} modified */ afterEdit: function(record, modifiedFieldNames, modified) { var me = this, data = me.data, currentId = modified[record.getIdProperty()] || record.getId(), currentIndex = data.keys.indexOf(currentId), newIndex; if (currentIndex === -1 && data.map[currentId] === undefined) { return; } if (me.getAutoSync()) { me.sync(); } if (currentId !== record.getId()) { data.replace(currentId, record); } else { data.replace(record); } newIndex = data.indexOf(record); if (currentIndex === -1 && newIndex !== -1) { me.fireEvent('addrecords', me, [record]); } else if (currentIndex !== -1 && newIndex === -1) { me.fireEvent('removerecords', me, [record], [currentIndex]); } else if (newIndex !== -1) { me.fireEvent('updaterecord', me, record, newIndex, currentIndex, modifiedFieldNames, modified); } }, /** * @private * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to. * @param {Ext.data.Model} record The model instance that was edited. */ afterReject: function(record) { var index = this.data.indexOf(record); this.fireEvent('updaterecord', this, record, index, index, [], {}); }, /** * @private * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to. * @param {Ext.data.Model} record The model instance that was edited. * @param {String[]} modifiedFieldNames * @param {Object} modified */ afterCommit: function(record, modifiedFieldNames, modified) { var me = this, data = me.data, currentId = modified[record.getIdProperty()] || record.getId(), currentIndex = data.keys.indexOf(currentId), newIndex; if (currentIndex === -1 && data.map[currentId] === undefined) { return; } if (currentId !== record.getId()) { data.replace(currentId, record); } else { data.replace(record); } newIndex = data.indexOf(record); if (currentIndex === -1 && newIndex !== -1) { me.fireEvent('addrecords', me, [record]); } else if (currentIndex !== -1 && newIndex === -1) { me.fireEvent('removerecords', me, [record], [currentIndex]); } else if (newIndex !== -1) { me.fireEvent('updaterecord', me, record, newIndex, currentIndex, modifiedFieldNames, modified); } }, /** * This gets called by a record after is gets erased from the server. * @param {Ext.data.Model} record * @private */ afterErase: function(record) { var me = this, data = me.data, index = data.indexOf(record); if (index !== -1) { data.remove(record); me.fireEvent('removerecords', me, [record], [index]); } }, applyRemoteFilter: function(value) { var proxy = this.getProxy(); return value || (proxy && proxy.isSQLProxy === true); }, applyRemoteSort: function(value) { var proxy = this.getProxy(); return value || (proxy && proxy.isSQLProxy === true); }, applyRemoteGroup: function(value) { var proxy = this.getProxy(); return value || (proxy && proxy.isSQLProxy === true); }, updateRemoteFilter: function(remoteFilter) { this.data.setAutoFilter(!remoteFilter); }, updateRemoteSort: function(remoteSort) { this.data.setAutoSort(!remoteSort); }, /** * Sorts the data in the Store by one or more of its properties. Example usage: * * // sort by a single field * myStore.sort('myField', 'DESC'); * * // sorting by multiple fields * myStore.sort([ * { * property : 'age', * direction: 'ASC' * }, * { * property : 'name', * direction: 'DESC' * } * ]); * * Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates * the actual sorting to its internal {@link Ext.util.Collection}. * * When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code: * * store.sort('myField'); * store.sort('myField'); * * is equivalent to this code: * * store.sort('myField', 'ASC'); * store.sort('myField', 'DESC'); * * because Store handles the toggling automatically. * * If the {@link #remoteSort} configuration has been set to `true`, you will have to manually call the {@link #method-load} * method after you sort to retrieve the sorted data from the server. * * @param {String/Ext.util.Sorter[]} sorters Either a string name of one of the fields in this Store's configured * {@link Ext.data.Model Model}, or an array of sorter configurations. * @param {String} [defaultDirection=ASC] The default overall direction to sort the data by. * @param {String} where (Optional) This can be either `'prepend'` or `'append'`. If you leave this undefined * it will clear the current sorters. */ sort: function(sorters, defaultDirection, where) { var data = this.data, grouper = this.getGrouper(), autoSort = data.getAutoSort(); if (sorters) { // While we are adding sorters we don't want to sort right away // since we need to update sortTypes on the sorters. data.setAutoSort(false); if (typeof where === 'string') { if (where == 'prepend') { data.insertSorters(grouper ? 1 : 0, sorters, defaultDirection); } else { data.addSorters(sorters, defaultDirection); } } else { data.setSorters(null); if (grouper) { data.addSorters(grouper); } data.addSorters(sorters, defaultDirection); } this.updateSortTypes(); // Setting back autoSort to true (if it was like that before) will // instantly sort the data again. data.setAutoSort(autoSort); } if (!this.getRemoteSort()) { // If we haven't added any new sorters we have to manually call sort if (!sorters) { this.data.sort(); } this.fireEvent('sort', this, this.data, this.data.getSorters()); if (data.length) { this.fireEvent('refresh', this, this.data); } } }, /** * Filters the loaded set of records by a given set of filters. * * Filtering by single field: * * store.filter("email", /\.com$/); * * Using multiple filters: * * store.filter([ * {property: "email", value: /\.com$/}, * {filterFn: function(item) { return item.get("age") > 10; }} * ]); * * Using Ext.util.Filter instances instead of config objects * (note that we need to specify the {@link Ext.util.Filter#root root} config option in this case): * * store.filter([ * Ext.create('Ext.util.Filter', {property: "email", value: /\.com$/, root: 'data'}), * Ext.create('Ext.util.Filter', {filterFn: function(item) { return item.get("age") > 10; }, root: 'data'}) * ]); * * If the {@link #remoteFilter} configuration has been set to `true`, you will have to manually call the {@link #method-load} * method after you filter to retrieve the filtered data from the server. * * @param {Object[]/Ext.util.Filter[]/String} filters The set of filters to apply to the data. * These are stored internally on the store, but the filtering itself is done on the Store's * {@link Ext.util.MixedCollection MixedCollection}. See MixedCollection's * {@link Ext.util.MixedCollection#filter filter} method for filter syntax. * Alternatively, pass in a property string. * @param {String} [value] value to filter by (only if using a property string as the first argument). * @param {Boolean} [anyMatch=false] `true` to allow any match, false to anchor regex beginning with `^`. * @param {Boolean} [caseSensitive=false] `true` to make the filtering regex case sensitive. */ filter: function(property, value, anyMatch, caseSensitive) { var data = this.data, filter = null; if (property) { if (Ext.isFunction(property)) { filter = {filterFn: property}; } else if (Ext.isArray(property) || property.isFilter) { filter = property; } else { filter = { property : property, value : value, anyMatch : anyMatch, caseSensitive: caseSensitive, id : property }; } } if (this.getRemoteFilter()) { data.addFilters(filter); } else { data.filter(filter); this.fireEvent('filter', this, data, data.getFilters()); this.fireEvent('refresh', this, data); } }, /** * Filter by a function. The specified function will be called for each * Record in this Store. If the function returns `true` the Record is included, * otherwise it is filtered out. * @param {Function} fn The function to be called. It will be passed the following parameters: * @param {Ext.data.Model} fn.record The {@link Ext.data.Model record} * to test for filtering. Access field values using {@link Ext.data.Model#get}. * @param {Object} fn.id The ID of the Record passed. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to this Store. */ filterBy: function(fn, scope) { var me = this, data = me.data, ln = data.length; data.filter({ filterFn: function(record) { return fn.call(scope || me, record, record.getId()); } }); this.fireEvent('filter', this, data, data.getFilters()); if (data.length !== ln) { this.fireEvent('refresh', this, data); } }, /** * Query the cached records in this Store using a filtering function. The specified function * will be called with each record in this Store. If the function returns `true` the record is * included in the results. * @param {Function} fn The function to be called. It will be passed the following parameters: * @param {Ext.data.Model} fn.record The {@link Ext.data.Model record} * to test for filtering. Access field values using {@link Ext.data.Model#get}. * @param {Object} fn.id The ID of the Record passed. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to this Store. * @return {Ext.util.MixedCollection} Returns an Ext.util.MixedCollection of the matched records. */ queryBy: function(fn, scope) { return this.data.filterBy(fn, scope || this); }, /** * Reverts to a view of the Record cache with no filtering applied. * @param {Boolean} [suppressEvent=false] `true` to clear silently without firing the `refresh` event. */ clearFilter: function(suppressEvent) { var ln = this.data.length; if (suppressEvent) { this.suspendEvents(); } this.data.setFilters(null); if (suppressEvent) { this.resumeEvents(true); } else if (ln !== this.data.length) { this.fireEvent('refresh', this, this.data); } }, /** * Returns `true` if this store is currently filtered. * @return {Boolean} */ isFiltered : function () { return this.data.filtered; }, /** * Returns `true` if this store is currently sorted. * @return {Boolean} */ isSorted : function () { return this.data.sorted; }, getSorters: function() { var sorters = this.data.getSorters(); return (sorters) ? sorters.items : []; }, getFilters: function() { var filters = this.data.getFilters(); return (filters) ? filters.items : []; }, /** * Returns an array containing the result of applying the grouper to the records in this store. See {@link #groupField}, * {@link #groupDir} and {@link #grouper}. Example for a store containing records with a color field: * * var myStore = Ext.create('Ext.data.Store', { * groupField: 'color', * groupDir : 'DESC' * }); * * myStore.getGroups(); //returns: * [ * { * name: 'yellow', * children: [ * //all records where the color field is 'yellow' * ] * }, * { * name: 'red', * children: [ * //all records where the color field is 'red' * ] * } * ] * * @param {String} groupName (Optional) Pass in an optional `groupName` argument to access a specific group as defined by {@link #grouper}. * @return {Object/Object[]} The grouped data. */ getGroups: function(requestGroupString) { var records = this.data.items, length = records.length, grouper = this.getGrouper(), groups = [], pointers = {}, record, groupStr, group, i; // <debug> if (!grouper) { Ext.Logger.error('Trying to get groups for a store that has no grouper'); } // </debug> for (i = 0; i < length; i++) { record = records[i]; groupStr = grouper.getGroupString(record); group = pointers[groupStr]; if (group === undefined) { group = { name: groupStr, children: [] }; groups.push(group); pointers[groupStr] = group; } group.children.push(record); } return requestGroupString ? pointers[requestGroupString] : groups; }, /** * @param {Ext.data.Model} record * @return {null} */ getGroupString: function(record) { var grouper = this.getGrouper(); if (grouper) { return grouper.getGroupString(record); } return null; }, /** * Finds the index of the first matching Record in this store by a specific field value. * @param {String} fieldName The name of the Record field to test. * @param {String/RegExp} value Either a string that the field value * should begin with, or a RegExp to test against the field. * @param {Number} startIndex (optional) The index to start searching at. * @param {Boolean} anyMatch (optional) `true` to match any part of the string, not just the beginning. * @param {Boolean} caseSensitive (optional) `true` for case sensitive comparison. * @param {Boolean} [exactMatch=false] (optional) `true` to force exact match (^ and $ characters added to the regex). * @return {Number} The matched index or -1 */ find: function(fieldName, value, startIndex, anyMatch, caseSensitive, exactMatch) { var filter = Ext.create('Ext.util.Filter', { property: fieldName, value: value, anyMatch: anyMatch, caseSensitive: caseSensitive, exactMatch: exactMatch, root: 'data' }); return this.data.findIndexBy(filter.getFilterFn(), null, startIndex); }, /** * Finds the first matching Record in this store by a specific field value. * @param {String} fieldName The name of the Record field to test. * @param {String/RegExp} value Either a string that the field value * should begin with, or a RegExp to test against the field. * @param {Number} startIndex (optional) The index to start searching at. * @param {Boolean} anyMatch (optional) `true` to match any part of the string, not just the beginning. * @param {Boolean} caseSensitive (optional) `true` for case sensitive comparison. * @param {Boolean} [exactMatch=false] (optional) `true` to force exact match (^ and $ characters added to the regex). * @return {Ext.data.Model} The matched record or `null`. */ findRecord: function() { var me = this, index = me.find.apply(me, arguments); return index !== -1 ? me.getAt(index) : null; }, /** * Finds the index of the first matching Record in this store by a specific field value. * @param {String} fieldName The name of the Record field to test. * @param {Object} value The value to match the field against. * @param {Number} startIndex (optional) The index to start searching at. * @return {Number} The matched index or -1. */ findExact: function(fieldName, value, startIndex) { return this.data.findIndexBy(function(record) { return record.get(fieldName) === value; }, this, startIndex); }, /** * Find the index of the first matching Record in this Store by a function. * If the function returns `true` it is considered a match. * @param {Function} fn The function to be called. It will be passed the following parameters: * @param {Ext.data.Model} fn.record The {@link Ext.data.Model record} * to test for filtering. Access field values using {@link Ext.data.Model#get}. * @param {Object} fn.id The ID of the Record passed. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to this Store. * @param {Number} startIndex (optional) The index to start searching at. * @return {Number} The matched index or -1. */ findBy: function(fn, scope, startIndex) { return this.data.findIndexBy(fn, scope, startIndex); }, /** * Loads data into the Store via the configured {@link #proxy}. This uses the Proxy to make an * asynchronous call to whatever storage backend the Proxy uses, automatically adding the retrieved * instances into the Store and calling an optional callback if required. Example usage: * * store.load({ * callback: function(records, operation, success) { * // the {@link Ext.data.Operation operation} object contains all of the details of the load operation * console.log(records); * }, * scope: this * }); * * If only the callback and scope options need to be specified, then one can call it simply like so: * * store.load(function(records, operation, success) { * console.log('loaded records'); * }, this); * * @param {Object/Function} [options] config object, passed into the {@link Ext.data.Operation} object before loading. * @param {Object} [scope] Scope for the function. * @return {Object} */ load: function(options, scope) { var me = this, operation, currentPage = me.currentPage, pageSize = me.getPageSize(); options = options || {}; if (Ext.isFunction(options)) { options = { callback: options, scope: scope || this }; } if (me.getRemoteSort()) { options.sorters = options.sorters || this.getSorters(); } if (me.getRemoteFilter()) { options.filters = options.filters || this.getFilters(); } if (me.getRemoteGroup()) { options.grouper = options.grouper || this.getGrouper(); } Ext.applyIf(options, { page: currentPage, start: (currentPage - 1) * pageSize, limit: pageSize, addRecords: false, action: 'read', params: this.getParams(), model: this.getModel() }); operation = Ext.create('Ext.data.Operation', options); if (me.fireEvent('beforeload', me, operation) !== false) { me.loading = true; me.getProxy().read(operation, me.onProxyLoad, me); } return me; }, /** * Returns `true` if the Store is currently performing a load operation. * @return {Boolean} `true` if the Store is currently loading. */ isLoading: function() { return Boolean(this.loading); }, /** * Returns `true` if the Store has been loaded. * @return {Boolean} `true` if the Store has been loaded. */ isLoaded: function() { return Boolean(this.loaded); }, /** * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated * and deleted records in the store, updating the Store's internal representation of the records * as each operation completes. * @return {Object} * @return {Object} return.added * @return {Object} return.updated * @return {Object} return.removed */ sync: function(options) { var me = this, operations = {}, toCreate = me.getNewRecords(), toUpdate = me.getUpdatedRecords(), toDestroy = me.getRemovedRecords(), needsSync = false; if (toCreate.length > 0) { operations.create = toCreate; needsSync = true; } if (toUpdate.length > 0) { operations.update = toUpdate; needsSync = true; } if (toDestroy.length > 0) { operations.destroy = toDestroy; needsSync = true; } if (needsSync && me.fireEvent('beforesync', this, operations) !== false) { me.getProxy().batch(Ext.merge({ operations: operations, listeners: me.getBatchListeners() }, options || {})); } return { added: toCreate, updated: toUpdate, removed: toDestroy }; }, /** * Convenience function for getting the first model instance in the store. * @return {Ext.data.Model/undefined} The first model instance in the store, or `undefined`. */ first: function() { return this.data.first(); }, /** * Convenience function for getting the last model instance in the store. * @return {Ext.data.Model/undefined} The last model instance in the store, or `undefined`. */ last: function() { return this.data.last(); }, /** * Sums the value of `property` for each {@link Ext.data.Model record} between `start` * and `end` and returns the result. * @param {String} field The field in each record. * @return {Number} The sum. */ sum: function(field) { var total = 0, i = 0, records = this.data.items, len = records.length; for (; i < len; ++i) { total += records[i].get(field); } return total; }, /** * Gets the minimum value in the store. * @param {String} field The field in each record. * @return {Object/undefined} The minimum value, if no items exist, `undefined`. */ min: function(field) { var i = 1, records = this.data.items, len = records.length, value, min; if (len > 0) { min = records[0].get(field); } for (; i < len; ++i) { value = records[i].get(field); if (value < min) { min = value; } } return min; }, /** * Gets the maximum value in the store. * @param {String} field The field in each record. * @return {Object/undefined} The maximum value, if no items exist, `undefined`. */ max: function(field) { var i = 1, records = this.data.items, len = records.length, value, max; if (len > 0) { max = records[0].get(field); } for (; i < len; ++i) { value = records[i].get(field); if (value > max) { max = value; } } return max; }, /** * Gets the average value in the store. * @param {String} field The field in each record you want to get the average for. * @return {Number} The average value, if no items exist, 0. */ average: function(field) { var i = 0, records = this.data.items, len = records.length, sum = 0; if (records.length > 0) { for (; i < len; ++i) { sum += records[i].get(field); } return sum / len; } return 0; }, /** * @private * Returns an object which is passed in as the listeners argument to `proxy.batch` inside `this.sync`. * This is broken out into a separate function to allow for customization of the listeners. * @return {Object} The listeners object. * @return {Object} return.scope * @return {Object} return.exception * @return {Object} return.complete */ getBatchListeners: function() { return { scope: this, exception: this.onBatchException, complete: this.onBatchComplete }; }, /** * @private * Attached as the `complete` event listener to a proxy's Batch object. Iterates over the batch operations * and updates the Store's internal data MixedCollection. */ onBatchComplete: function(batch) { var me = this, operations = batch.operations, length = operations.length, i; for (i = 0; i < length; i++) { me.onProxyWrite(operations[i]); } }, onBatchException: function(batch, operation) { // //decide what to do... could continue with the next operation // batch.start(); // // //or retry the last operation // batch.retry(); }, /** * @private * Called internally when a Proxy has completed a load request. */ onProxyLoad: function(operation) { var me = this, records = operation.getRecords(), resultSet = operation.getResultSet(), successful = operation.wasSuccessful(); if (resultSet) { me.setTotalCount(resultSet.getTotal()); } if (successful) { this.fireAction('datarefresh', [this, this.data, operation], 'doDataRefresh'); } me.loaded = true; me.loading = false; me.fireEvent('load', this, records, successful, operation); //this is a callback that would have been passed to the 'read' function and is optional Ext.callback(operation.getCallback(), operation.getScope() || me, [records, operation, successful]); }, doDataRefresh: function(store, data, operation) { var records = operation.getRecords(), me = this, destroyRemovedRecords = me.getDestroyRemovedRecords(), currentRecords = data.all.slice(), ln = currentRecords.length, ln2 = records.length, ids = {}, i, record; if (operation.getAddRecords() !== true) { for (i = 0; i < ln2; i++) { ids[records[i].id] = true; } for (i = 0; i < ln; i++) { record = currentRecords[i]; record.unjoin(me); // If the record we are removing is not part of the records we are about to add to the store then handle // the destroying or removing of the record to avoid memory leaks. if (ids[record.id] !== true && destroyRemovedRecords && !record.stores.length) { record.destroy(); } } data.clear(); // This means we have to fire a clear event though me.fireEvent('clear', me); } if (records && records.length) { // Now lets add the records without firing an addrecords event me.suspendEvents(); me.add(records); me.resumeEvents(true); // true to discard the queue } me.fireEvent('refresh', me, data); }, /** * @private * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect * the updates provided by the Proxy. */ onProxyWrite: function(operation) { var me = this, success = operation.wasSuccessful(), records = operation.getRecords(); switch (operation.getAction()) { case 'create': me.onCreateRecords(records, operation, success); break; case 'update': me.onUpdateRecords(records, operation, success); break; case 'destroy': me.onDestroyRecords(records, operation, success); break; } if (success) { me.fireEvent('write', me, operation); } //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional Ext.callback(operation.getCallback(), operation.getScope() || me, [records, operation, success]); }, // These methods are now just template methods since updating the records etc is all taken care of // by the operation itself. onCreateRecords: function(records, operation, success) {}, onUpdateRecords: function(records, operation, success) {}, onDestroyRecords: function(records, operation, success) { this.removed = []; }, onMetaChange: function(data) { var model = this.getProxy().getModel(); if (!this.getModel() && model) { this.setModel(model); } /** * @event metachange * Fires whenever the server has sent back new metadata to reconfigure the Reader. * @param {Ext.data.Store} this * @param {Object} data The metadata sent back from the server. */ this.fireEvent('metachange', this, data); }, /** * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one). * @return {Ext.data.Model[]} The Model instances. */ getNewRecords: function() { return this.data.filterBy(function(item) { // only want phantom records that are valid return item.phantom === true && item.isValid(); }).items; }, /** * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy. * @return {Ext.data.Model[]} The updated Model instances. */ getUpdatedRecords: function() { return this.data.filterBy(function(item) { // only want dirty records, not phantoms that are valid return item.dirty === true && item.phantom !== true && item.isValid(); }).items; }, /** * Returns any records that have been removed from the store but not yet destroyed on the proxy. * @return {Ext.data.Model[]} The removed Model instances. */ getRemovedRecords: function() { return this.removed; }, // PAGING METHODS /** * Loads a given 'page' of data by setting the start and limit values appropriately. Internally this just causes a normal * load operation, passing in calculated `start` and `limit` params. * @param {Number} page The number of the page to load. * @param {Object} options See options for {@link #method-load}. * @param {Object} scope */ loadPage: function(page, options, scope) { if (typeof options === 'function') { options = { callback: options, scope: scope || this }; } var me = this, pageSize = me.getPageSize(), clearOnPageLoad = me.getClearOnPageLoad(); options = Ext.apply({}, options); me.currentPage = page; me.load(Ext.applyIf(options, { page: page, start: (page - 1) * pageSize, limit: pageSize, addRecords: !clearOnPageLoad })); }, /** * Loads the next 'page' in the current data set. * @param {Object} options See options for {@link #method-load}. */ nextPage: function(options) { this.loadPage(this.currentPage + 1, options); }, /** * Loads the previous 'page' in the current data set. * @param {Object} options See options for {@link #method-load}. */ previousPage: function(options) { this.loadPage(this.currentPage - 1, options); }, destroy: function() { this.clearData(); var proxy = this.getProxy(); if (proxy) { proxy.onDestroy(); } Ext.data.StoreManager.unregister(this); Ext.destroy(this.getPlugins()); if (this.implicitModel && this.getModel()) { delete Ext.data.ModelManager.types[this.getModel().getName()]; } Ext.destroy(this.data); this.callParent(arguments); } // <deprecated product=touch since=2.0> ,onClassExtended: function(cls, data) { var prototype = this.prototype, defaultConfig = prototype.config, config = data.config || {}, key; // Convert deprecated properties in application into a config object for (key in defaultConfig) { if (key != "control" && key in data) { config[key] = data[key]; delete data[key]; // <debug warn> Ext.Logger.deprecate(key + ' is deprecated as a property directly on the ' + this.$className + ' prototype. Please put it inside the config object.'); // </debug> } } data.config = config; } }, function() { /** * Loads an array of data straight into the Store. * @param {Ext.data.Model[]/Object[]} data Array of data to load. Any non-model instances will be cast into model instances first. * @param {Boolean} append `true` to add the records to the existing records in the store, `false` to remove the old ones first. * @deprecated 2.0 Please use #add or #setData instead. * @method loadData */ this.override({ loadData: function(data, append) { Ext.Logger.deprecate("loadData is deprecated, please use either add or setData"); if (append) { this.add(data); } else { this.setData(data); } }, //@private doAddListener: function(name, fn, scope, options, order) { // <debug> switch(name) { case 'update': Ext.Logger.warn('The update event on Store has been removed. Please use the updaterecord event from now on.'); return this; case 'add': Ext.Logger.warn('The add event on Store has been removed. Please use the addrecords event from now on.'); return this; case 'remove': Ext.Logger.warn('The remove event on Store has been removed. Please use the removerecords event from now on.'); return this; case 'datachanged': Ext.Logger.warn('The datachanged event on Store has been removed. Please use the refresh event from now on.'); return this; break; } // </debug> return this.callParent(arguments); } }); /** * @member Ext.data.Store * @method loadRecords * @inheritdoc Ext.data.Store#add * @deprecated 2.0.0 Please use {@link #add} instead. */ Ext.deprecateMethod(this, 'loadRecords', 'add', "Ext.data.Store#loadRecords has been deprecated. Please use the add method."); // </deprecated> });


04:00
Lassú
Közepes
Gyors
This is a simulation of 1,000 people's average day. he
ATUS Extract Builder
.


Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
This text is blue. This text is red. This text is green. This is an internal link. This is an external link. This is an email link. This text is bold. This text is italic.
Text above the image. Text below the image.
The image below is a link. The image above is a link.
| Cell | Cell | Cell | Cell |
| Cell | Cell | Cell | Cell |
| Cell | Cell | Cell | Cell |
| Cell | Cell | Cell | Cell |
| Cell | Cell | Cell | Cell |


In simplest possible way, recursive function is a function that calls itself. It’s common for function to call another function, but when it calls itself its a recursive function. Recursive method calls itself so many times until being satisfied, this method should call itself with new parameters to avoid infinite loop.
We will use Fibonacci sequence as an an example.
There are two ways of creating Fibonacci sequence in as an recursive function. We will take a look at both of them.
This is the first option with three parameters, first two represent numbers to add, third is a counter of iterations, last one is passed by the user and represent the length of the series.
private static void FibonacciSequenceTemp(int a, int b, int counter,int len) { if (counter <= len) { Console.Write(a + " "); FibonacciSequenceTemp(b, a + b, counter+1, len); } }
We can call this function directly or to simplify the interface we can create a function with one input parameter.
public static void FibonacciSequence(int len) { FibonacciSequenceTemp(0, 1, 1, len); }
This way the function can be executed with different parameters each iteration, and we don’t need to specify conditions for low iteration numbers like 0 or 1, which we will do in another example;
Another way of dealing with this problem is to create with function with one input that will be called twice and will represent numbers to add. In this example we need to include conditions like n=0 or n=1, because this function works only for argument grater than one
static void Main(string[] args) { int n, i = 0; //n is a number of elements n=5; //loop for pinting all values for ( int c = 1 ; c <= n ; c++ ) { Console.WriteLine(Fibonacci(i)); i++; } Console.ReadKey(); } static int Fibonacci(int n) { if (n == 0) return 0; else if (n == 1) return 1; else return (Fibonacci(n - 1) + Fibonacci(n - 2)); }
In both examples we are calling the function within itself, what makes it recursive. we need to remember to change the input parameters with every iteration to not stuck in infinite loop. Fibonacci sequence can be created without using recursive functions, but it is a good demonstration of practical use of it. In next article I’ll cover another ways of dealing with Fibonacci sequence. Stay Awesome!


+27 61 136 3956
<EMAIL_ADDRESS>Are you building a double story house or a flat? Well guess what, we specialize in laying slabs and building stairs cases
We start by checking the location, the amount of work required to complete the project and account for all obsticals that maybe encountered in order to provide a realistic time frame and making sure that we produce a quality service.
After we have assembled the skeleton of the slab or stairs we consult width engineers to make sure that we meet the safty and usability requirements
After we have laid the slab or stairs our job is not done yet. we now provide support to the other constrctors if there are any problems or defects we make sure that we address those issues effeciantly and quickly
The company was started in 2011 by Lucas Kgomo. who saw a business opportunity in the growing need of buiding double story houses. The trend came about in South African's realising that in order to extend their homes they needed to build up because there is little space within the yard. There was now a market for providing the most essential part of buiding up which is laying slabs and stairs.
To expand and spread our services accross the country to help home owners build their dream homes and help business and institues to reach greater heights
View
View
View
View
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas dignissimos dolore quaerat in molestias dolorum numquam sed id maiores fuga!
For any questions or ideas you would like to share, send a message
+27<PHONE_NUMBER>
<EMAIL_ADDRESS>Rama City house No 650
Aksia
f
G


An endpoint is anything you want to monitor.
When you enter the fully qualified domain name of your endpoint, worldPing will automatically detect what is available to monitor (eg. PING, HTTP, HTTPS) and suggest a customizable set of defaults unique to your endpoint.
We're contacting your endpoint from your selected probes around the globe.
Based on your config, it can take up to ~{{ctrl.endpointReadyDelay}} seconds for data to start appearing in your dashboard.


{% extends "basebase.html" %} {% block styles %} {% endblock %} {% block content %}
| ID | Phase | Description | Assessment Year | Emp-Email | Start-Date | End-Date | Auth-level | Gs-enable-assignment | Gs-enable-MidYear-Self | Gs-enable-MidYear-dc-approve | Gs-enable-EndYear-Self | Gs-enable-EndYear-dc-approve | Gs-enable-EndYear-closure | Goal-enable-Task-approve | Goal-enable-edit | Goal-enable-approve | Task-enable-approve | Task-enable-update | Task-enable-delete | Task-enable-Activity-edit | Task-enable-Status-change | Phase Type |
| ID | Name(Program) | Start Date | End Date | Manager-Email | Contract-Company | Contract-Region | Contract-Entity | Description | ||||||||||||||
| ID | Phase | Description | Assessment Year | Emp-Email | Start-Date | End-Date | Auth-level | Gs-enable-assignment | Gs-enable-MidYear-Self | Gs-enable-MidYear-dc-approve | Gs-enable-EndYear-Self | Gs-enable-EndYear-dc-approve | Gs-enable-EndYear-closure | Goal-enable-Task-approve | Goal-enable-edit | Goal-enable-approve | Task-enable-approve | Task-enable-update | Task-enable-delete | Task-enable-Activity-edit | Task-enable-Status-change | Phase Type |


Changelog¶

 v0.3.19	 Fix instrumenter for multiple blank array positions, thanks @alexdunphy

 v0.3.18	 Upgrade esprima, get support for more ES6 features

 v0.3.17	 Upgrade esprima, get correct for-of support

 v0.3.16
-

-
upgrades to filset and async modules, thanks to @roderickhsiao, @popomore

updated text reporter so that it displays a list of the lines missing coverage, thanks @bcoe

 v0.3.15
-

-
Fix #375: add nodir option to exclude directory for *.js matcher thanks to @yurenju

Fix #362: When setting up the reportDir add it to reporter.dir`</li> <li>Fixes #238 (added a poorman’s clone)</li> <li>Incrementing hits on ignored statements implemented</li> <li>`a:visited color: #777 (a nice gray color)

 v0.3.14
Add text-lcov report format to emit lcov to console, thanks to @bcoe

 v0.3.13
Fix #339

 v0.3.12
Allow other-than-dot-js files to be hooked, thanks to @sethpollack

 v0.3.11
Avoid modification of global objects, thanks to @dominykas

 v0.3.10
Update escodegen to 1.6.x and add browser download script

 v0.3.9
-

-
Merge harmony branch and start adding ES6 features to istanbul

Arrow functions are the only feature of interest now

`for-of` and yield support exist but not present in mainline esprima yet

 v0.3.8
-

-
Fail check coverage command when no coverage files found, thanks to @nexus-uw

handle relative paths in check-coverage, thanks to @dragn

support explicit includes for cover, thanks to @tonylukasavage

 v0.3.7
Fix asset paths on windows, thanks to @juangabreil

 v0.3.6
-

-
Update to Esprima 2.0

Remove YUI dependency and provide custom sort code. No network access needed for HTML report view

use supports-color module to colorize output, thanks to @gustavnikolaj

Fix tests to work on Windows, thanks to @dougwilson

Docs: “Instrument code” API example correction thanks to @robatron

Extracted embedded CSS and JavaScript and made them external files, thanks to @booleangate

 v0.3.5
Merge #275 - –include-all-sources option. Thanks @gustavnikolaj

 The –preload-sources option is now deprecated and superseded by the –include-all-sources option instead. This provides a better coverage representation of the code that has not been included for testing.

 v0.3.4	 Merge #219 - Support reporting within symlink/junction. Thanks to @dougwilson

 v0.3.3	 Merge #268 - per file coverage enforcement. Thanks to @ryan-roemer

 v0.3.2	 Republish 0.3.1 because of bad shasum

 v0.3.1	 Fixes #249

 v0.3.0
The reports release. Potentially backwards-incompatible if you are using undocumented features or custom report implementations.

Change report command line to support multiple reports, add back-compat processing with warnings

Enable report command to read report list from config, thanks to @piuccio

Support multiple reports for cover and report commands

Support per-report config options in configuration file

Turn reports into event emitters so they can signal done`</li> <li>Add `Reporter class to be able to generate multiple reports

Add a bunch of API docs, refactor README

 v0.2.16	Make YUI links https-always since relative links break local filesystem use-case

 v0.2.15	make link protocols relative so they don’t break on https connections (thanks to @yasyf)

 v0.2.14	Fix hook to deal with non-string/ missing filenames (thanks to @jason0x43), update dependencies

 v0.2.13	Add –preload-sources option to cover command to make code not required by tests to appear in the coverage report.

 v0.2.12	Text summary as valid markdown, thanks to @smikes

 v0.2.11	Allow source map generation, thanks to @jason0x43

 v0.2.10	Add flag to handle sigints and dump coverage, thanks to @samccone

 v0.2.9	Fix #202

 v0.2.8	Upgrade esprima

 v0.2.7

Upgrade esprima

Misc jshint fixes

 v0.2.6

Revert bad commit for tree summarizer

 v0.2.5

Add clover report, thanks to @bixdeng, @mpderbec

Fix cobertura report bug for relative paths, thanks to @jxiaodev

Run self-coverage on tests always

Fix tree summarizer when relative paths are involved, thanks to @Swatinem

 v0.2.4

Fix line-split algo to handle Mac lin separators, thanks to @asifrc

Update README for quick intro to ignoring code for coverage, thanks to @gergelyke

 v0.2.3

Add YAML config file. istanbul help config has more details

Support custom reporting thresholds using the watermarks section of the config file

v0.2.2	update escodegen, handlebars and resolve dependency versions

 v0.2.1

-
Add ability to skip branches and other hard-to-test code using comments.
- See the doc for more details

Turn util.error into console.error for node 0.11 compatibility, thanks to @pornel

v0.2.0

Add –preserve-comments to instrumenter options, thanks to @arikon

Support ‘use strict;’ in file scope, thanks to @pornel

-

- Up minor version due to the new way in which the global object is accessed. This _should_ be backwards-compatible but has not been tested in the wild.

v0.1.46	Fix #114

v0.1.45	Add teamcity reporter, thanks to @chrisgladd

v0.1.44	Fix inconsistency in processing empty switch with latest esprima, up deps

v0.1.43	Add colors to text report thanks to @runk

v0.1.42	fix #78: embed source regression introduced in v0.1.38. Fix broken test for this

v0.1.41	add json report to dump coverage object for certain use cases

v0.1.40	forward sourceStore from lcov to html report, pull request by @vojtajina

v0.1.39	add <source> tag to cobertura report, pull request by @jhansche

v0.1.38

factor out AST instrumentation into own instrumentASTSync method

always set function declaration coverage stats to 1 since every such declaration is “executed” exactly one time by the compiler

v0.1.37	–complete-copy flag contrib from @kami, correct strict mode semantics for instrumented functions

v0.1.36	real quiet when –print=none specified, add repo URL to package.json, add contributors

v0.1.35	accept cobertura contrib from @nbrownus, fix #52

v0.1.34	fix async reporting, update dependencies, accept html cleanup contrib from @mathiasbynens

v0.1.33	initialize global coverage object before running tests to workaround mocha leak detection

v0.1.32	Fix for null nodes in array expressions, add @unindented as contributor

v0.1.31	Misc internal fixes and test changes

v0.1.30	Write standard blurbs (“writing coverage object...” etc.) to stderr rather than stdout

v0.1.29	Allow –post-require-hook to be a module that can be require-d

v0.1.28	Add –post-require-hook switch to support use-cases similar to the YUI loader

v0.1.27	Add –hook-run-in-context switch to support RequireJS modules. Thanks to @millermedeiros for the pull request

v0.1.26	Add support for minimum uncovered unit for check-coverage. Fixes #25

v0.1.25	Allow for relative paths in the YUI loader hook

v0.1.24	Add lcov summaries. Fixes issue #20

v0.1.23	Add ability to save a baseline coverage file for the instrument command. Fixes issue #19

v0.1.22	Add signature attribute to cobertura method tags to fix NPE by the Hudson publisher

v0.1.21	Add cobertura XML report format; exprimental for now

v0.1.20	Fix HTML/ lcov report interface to be more customizable for middleware needs

v0.1.19	make all hooking non-destructive in that already loaded modules are never reloaded. Add self-test mode so that already loaded istanbul modules can be unloaded prior to hooking.

v0.1.18	Add option to hook in non-destructive mode; i.e. the require cache is not unloaded when hooking

v0.1.17	Export some more objects; undocumented for now

v0.1.16	Fix npm keywords for istanbul which expects an array of strings but was being fed a single string with keywords instead

v0.1.15	Add the ‘check-coverage’ command so that Istanbul can be used as a posttest script to enforce minimum coverage

v0.1.14	Expose the experimental YUI load hook in the interface

v0.1.13	Internal jshint cleanup, no features or fixes

v0.1.12	Give npm the README that was getting inadvertently excluded

v0.1.11	Merge pull request #14 for HTML tweaks. Thanks @davglass. Add @davglass and @nowamasa as contributors in package.json`</td></tr> <tr><td>v0.1.10</td><td>Fix to issue #12. Do not install `uncaughtException handler and pass input error back to CLI using a callback as opposed to throwing.

v0.1.9	Attempt to create reporting directory again just before writing coverage in addition to initial creation

v0.1.8	Fix issue #11.

v0.1.7	Add text summary and detailed reporting available as –print [summary|detail|both|none]. summary is the default if nothing specified.

v0.1.6	Handle backslashes in the file path correctly in emitted code. Fixes #9. Thanks to @nowamasa for bug report and fix

v0.1.5	make object-utils.js work on a browser as-is

v0.1.4	partial fix for issue #4; add titles to missing coverage spans, remove negative margin for missing if/else indicators

v0.1.3	Set the environment variable running_under_istanbul to 1 when that is the case. This allows test runners that use istanbul as a library to back off on using it when set.

v0.1.2	HTML reporting cosmetics. Reports now show syntax-colored JS using prettify. Summary tables no longer wrap in awkward places.

v0.1.1	Fixes issue #1. HTML reports use sources embedded inside the file coverage objects if found rather than reading from the filesystem

v0.1.0	Initial version


To use Weather Underground, obtain an API key from www.wunderground.com/weather/api/ and paste it below.


Contains the scheduling functionality which allows for dynamic kernel generation as well as the fusion of multiple statements into a single kernel.
More...
Namespaces |
| namespace | detail |
| | Implementation details for the scheduler.
|
| namespace | result_of |
| | Helper metafunctions used for the scheduler.
|
Data Structures |
| class | statement_not_supported_exception |
| | Exception for the case the scheduler is unable to deal with the operation. More...
|
| struct | lhs_rhs_element |
| | A class representing the 'data' for the LHS or RHS operand of the respective node. More...
|
| struct | op_element |
| | Struct for holding the type family as well as the type of an operation (could be addition, subtraction, norm, etc.) More...
|
| struct | statement_node |
| | Main datastructure for an node in the statement tree. More...
|
| class | statement |
| | The main class for representing a statement such as x = inner_prod(y,z); at runtime. More...
|
Enumerations |
| enum | operation_node_type_family { OPERATION_INVALID_TYPE_FAMILY = 0,
OPERATION_UNARY_TYPE_FAMILY,
OPERATION_BINARY_TYPE_FAMILY
} |
| | Optimization enum for grouping operations into unary or binary operations. Just for optimization of lookups. More...
|
| enum | operation_node_type {
OPERATION_INVALID_TYPE = 0,
OPERATION_UNARY_ABS_TYPE,
OPERATION_UNARY_ACOS_TYPE,
OPERATION_UNARY_ASIN_TYPE,
OPERATION_UNARY_ATAN_TYPE,
OPERATION_UNARY_CEIL_TYPE,
OPERATION_UNARY_COS_TYPE,
OPERATION_UNARY_COSH_TYPE,
OPERATION_UNARY_EXP_TYPE,
OPERATION_UNARY_FABS_TYPE,
OPERATION_UNARY_FLOOR_TYPE,
OPERATION_UNARY_LOG_TYPE,
OPERATION_UNARY_LOG10_TYPE,
OPERATION_UNARY_SIN_TYPE,
OPERATION_UNARY_SINH_TYPE,
OPERATION_UNARY_SQRT_TYPE,
OPERATION_UNARY_TAN_TYPE,
OPERATION_UNARY_TANH_TYPE,
OPERATION_UNARY_TRANS_TYPE,
OPERATION_UNARY_NORM_1_TYPE,
OPERATION_UNARY_NORM_2_TYPE,
OPERATION_UNARY_NORM_INF_TYPE,
OPERATION_BINARY_ACCESS_TYPE,
OPERATION_BINARY_ASSIGN_TYPE,
OPERATION_BINARY_INPLACE_ADD_TYPE,
OPERATION_BINARY_INPLACE_SUB_TYPE,
OPERATION_BINARY_ADD_TYPE,
OPERATION_BINARY_SUB_TYPE,
OPERATION_BINARY_MAT_VEC_PROD_TYPE,
OPERATION_BINARY_MAT_MAT_PROD_TYPE,
OPERATION_BINARY_MULT_TYPE,
OPERATION_BINARY_DIV_TYPE,
OPERATION_BINARY_ELEMENT_PROD_TYPE,
OPERATION_BINARY_ELEMENT_DIV_TYPE,
OPERATION_BINARY_INNER_PROD_TYPE
} |
| | Enumeration for identifying the possible operations. More...
|
| enum | statement_node_type_family {
INVALID_TYPE_FAMILY = 0,
COMPOSITE_OPERATION_FAMILY,
SCALAR_TYPE_FAMILY,
VECTOR_TYPE_FAMILY,
MATRIX_TYPE_FAMILY
} |
| | Groups the type of a node in the statement tree. Used for faster dispatching. More...
|
| enum | statement_node_subtype {
INVALID_SUBTYPE = 0,
HOST_SCALAR_TYPE,
DEVICE_SCALAR_TYPE,
DENSE_VECTOR_TYPE,
IMPLICIT_VECTOR_TYPE,
DENSE_ROW_MATRIX_TYPE,
DENSE_COL_MATRIX_TYPE,
IMPLICIT_MATRIX_TYPE,
COMPRESSED_MATRIX_TYPE,
COORDINATE_MATRIX_TYPE,
ELL_MATRIX_TYPE,
HYB_MATRIX_TYPE
} |
| | Encodes the type of a node in the statement tree. More...
|
| enum | statement_node_numeric_type {
INVALID_NUMERIC_TYPE = 0,
CHAR_TYPE,
UCHAR_TYPE,
SHORT_TYPE,
USHORT_TYPE,
INT_TYPE,
UINT_TYPE,
LONG_TYPE,
ULONG_TYPE,
HALF_TYPE,
FLOAT_TYPE,
DOUBLE_TYPE
} |
| | Encodes the type of a node in the statement tree. More...
|
Functions |
| void | execute (statement const &s) |
| void | execute_element_composite (statement const &s, statement_node const &root_node) |
| | Deals with x = RHS where RHS is a vector expression.
|
| void | execute_matrix_prod (statement const &s, statement_node const &root_node) |
| void | execute_scalar_assign_composite (statement const &s, statement_node const &root_node) |
| | Deals with x = RHS where RHS is a vector expression.
|
| std::ostream & | operator<< (std::ostream &os, viennacl::scheduler::statement_node const &s_node) |
| | Print a single statement_node. Non-recursive.
|
| std::ostream & | operator<< (std::ostream &os, viennacl::scheduler::statement const &s) |
| | Writes a string identifying the scheduler statement to an output stream.
|
Contains the scheduling functionality which allows for dynamic kernel generation as well as the fusion of multiple statements into a single kernel.


-
Confirmed cases: 321386
-
Deaths confirmed: 4505
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 164565
-
Deaths confirmed: 1747
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 145320
-
Deaths confirmed: 1998
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 5273
-
Deaths confirmed: 72
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 2147
-
Deaths confirmed: 18
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 79340
-
Deaths confirmed: 1268
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 23375765
-
Deaths confirmed: 376827
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 30915328
-
Deaths confirmed: 724809
*Numbers reported as of Feb 4th 2021
-
Confirmed cases: 16217177
-
Deaths confirmed: 423622
*Numbers reported as of Feb 4th 2021


I’m a web developer working in HTML, CSS, JavaScript, Ruby on Rails & MERN Stack (MongoDB, Express.js, React.js
and Node.js). I formerly worked as a Chef where I learned organizational skills as well as leadership skills
in a fast paced environment. I’m currently working on adding new features to my React App, FOOD MOOD, as
well as creating new React apps, such as Lite Brite and Tic-Tac-Toe written in React!
Creating useful and fun apps is what I enjoy to do and hopefully you'll enjoy using them as well!
Technical Skills
Languages
HTML
CSS
JavaScript
Ruby
jQuery
Frameworks
React.js
Node.js
Express.js
Ruby on Rails
Bootstrap
Databases
MongoDB
PostgreSQL
Portfolio
Food Mood
React | MongoDB | Express | Node
Star Wars Slot Machine
HTML | CSS | JavaScript
Eat Me Recipes
Ruby on Rails
Gobbler
Express | Node | Mongoose | HTML | CSS
(Team Project)
Tic-Tac-Toe (React)
HTML | CSS | JavaScript | React.JS
Contact
Feel free to contact me if you have any further questions!
This is a Star Wars Slot Machine web-app that I built using HTML, CSS, and JavaScript. This project was my
project 1 at General Assembly. I hope you enjoy using and playing my app.
Eat Me is a recipe storing/sharing app to help the professional or home cook save their favorite recipes
or look up what other cooks/chefs have done. Eat Me was written in Ruby on Rails and was my Project 2 at
General Assembly
Gobbler is a social media app designed to be a Twitter clone but with an edit button on your 'Gobbles' (Tweets).
This was my project 3 at General Assembly and it was a group project. Out of our group of 4, I was in charge
of creating the routes, adding OAuth and helping out with design.
FOOD MOOD is a React app that uses the full MERN stack. This app suggests restaurants to eat around you according
to the weather. Food Mood is uses Yelp's API and Open Weather Map's API to gather information needed and
meshes it together to return the required results. This was my 4th and final project as a student at General
Assembly.
This Lite-Brite app was created using React.JS. It is still a working progress and will be completed soon.
Check back here soon to see the final app in action.


Being an enthusiastic engineer, I am a full-stack web developer with a specialisation in back-end. Beyond software development, I love indulging in mathematics and complex algorithms questions.
Mumblecore hexagon kombucha, pitchfork four loko raclette intelligentsia master cleanse. Vinyl XOXO lumbersexual
I am experienced with Java and NodeJS as back-end languages, and familiar with C++ as well.
Mumblecore hexagon kombucha, pitchfork four loko raclette intelligentsia master cleanse. Vinyl XOXO lumbersexual
Developed a full-stack chrome application using JavaScript, HTML & CSS to route print requests to a Label Printer. To be sold to external clients. Developed a system to visualise Kafka and Internal System metrics using Grafana, InfluxDB and Prometheus. Integrated Highly Structured Message logic into their WhatsApp Service.
Built a face-swapping telegram bot using Python and OpenCV.
Check it outUsed auto-caffe 2 to build a model that predicts water levels one hour in advance based on current rainfall, in Singapore for an annual NUS datathon. We, as a team, won the overall second place and were the only freshmen winners.


Error while changing user {{index}} settings: {{error}}
{% endif %} {% endif %} {% set deviceStatus = pluginObject._getorset(device, module=module, protocol=protocol, credentials=credentials, port=port, noverify=noverify, includeConfig=True, token=token) %} {% set device = deviceStatus.device %} {% set config = deviceStatus.get('config', {}) %} {% set userStatus = deviceStatus.get(module, {}) %} {% set users = userStatus.users %} {% set message = deviceStatus.message %} {% if message %}Message: {{message}}
{% if message[:4] == '502 ' %}If the SSL certificate of iotsa device "{{device}}" is not trusted by this Igor please try to connect using the form at the bottom of the iotsa device list page and select the Do not check SSL certificates option.
{% elif message[:4] == '401 ' %}To allow access to module "users" iotsa device "{{device}}" requires username/passord credentials. You can use the latter by selecting this device on the form at the bottom of iotsa device list and providing them in the credentials field.
Note that for the users module capability-based access does not work, you must specify username/password credentials.
{% endif %} {% endif %}| # | Key | Value |
|---|---|---|
| {{loop.index0}} | {% endif %}{{k}} | {{v}} |
Device {{device}} is in configuration mode, you have {{config.modeTimeout}} seconds to change user settings.
{% elif config.requestedMode == 1 %}You have requested configuration mode, but not rebooted {{device}} yet. Please do so within {{config.requestedModeTimeout}} seconds and select refresh to reload this page.
{% else %}To change usernames or passwords {{device}} must be in configuration mode. You can request the device to go to configuration mode on the device status page.
{% endif %} {% for user in users %}