js_stack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ //= require js_stack/backbone.pageable/backbone.pageable-1.3.2
@@ -0,0 +1,463 @@
1
+ (function($) {
2
+
3
+ // Backbone.Stickit Namespace
4
+ // --------------------------
5
+
6
+ Backbone.Stickit = {
7
+
8
+ _handlers: [],
9
+
10
+ addHandler: function(handlers) {
11
+ // Fill-in default values.
12
+ handlers = _.map(_.flatten([handlers]), function(handler) {
13
+ return _.extend({
14
+ updateModel: true,
15
+ updateView: true,
16
+ updateMethod: 'text'
17
+ }, handler);
18
+ });
19
+ this._handlers = this._handlers.concat(handlers);
20
+ }
21
+ };
22
+
23
+ // Backbone.View Mixins
24
+ // --------------------
25
+
26
+ _.extend(Backbone.View.prototype, {
27
+
28
+ // Collection of model event bindings.
29
+ // [{model,event,fn}, ...]
30
+ _modelBindings: null,
31
+
32
+ // Unbind the model and event bindings from `this._modelBindings` and
33
+ // `this.$el`. If the optional `model` parameter is defined, then only
34
+ // delete bindings for the given `model` and its corresponding view events.
35
+ unstickit: function(model) {
36
+ _.each(this._modelBindings, _.bind(function(binding, i) {
37
+ if (model && binding.model !== model) return false;
38
+ binding.model.off(binding.event, binding.fn);
39
+ delete this._modelBindings[i];
40
+ }, this));
41
+ this._modelBindings = _.compact(this._modelBindings);
42
+
43
+ this.$el.off('.stickit' + (model ? '.' + model.cid : ''));
44
+ },
45
+
46
+ // Using `this.bindings` configuration or the `optionalBindingsConfig`, binds `this.model`
47
+ // or the `optionalModel` to elements in the view.
48
+ stickit: function(optionalModel, optionalBindingsConfig) {
49
+ var self = this,
50
+ model = optionalModel || this.model,
51
+ namespace = '.stickit.' + model.cid,
52
+ bindings = optionalBindingsConfig || this.bindings || {};
53
+
54
+ this._modelBindings || (this._modelBindings = []);
55
+ this.unstickit(model);
56
+
57
+ // Iterate through the selectors in the bindings configuration and configure
58
+ // the various options for each field.
59
+ _.each(_.keys(bindings), function(selector) {
60
+ var $el, options, modelAttr, config,
61
+ binding = bindings[selector] || {},
62
+ bindKey = _.uniqueId();
63
+
64
+ // Support ':el' selector - special case selector for the view managed delegate.
65
+ if (selector != ':el') $el = self.$(selector);
66
+ else {
67
+ $el = self.$el;
68
+ selector = '';
69
+ }
70
+
71
+ // Fail fast if the selector didn't match an element.
72
+ if (!$el.length) return;
73
+
74
+ // Allow shorthand setting of model attributes - `'selector':'observe'`.
75
+ if (_.isString(binding)) binding = {observe:binding};
76
+
77
+ config = getConfiguration($el, binding);
78
+
79
+ modelAttr = config.observe;
80
+
81
+ // Create the model set options with a unique `bindKey` so that we
82
+ // can avoid double-binding in the `change:attribute` event handler.
83
+ options = _.extend({bindKey:bindKey}, config.setOptions || {});
84
+
85
+ initializeAttributes(self, $el, config, model, modelAttr);
86
+
87
+ initializeVisible(self, $el, config, model, modelAttr);
88
+
89
+ if (modelAttr) {
90
+ // Setup one-way, form element to model, bindings.
91
+ _.each(config.events || [], function(type) {
92
+ var event = type + namespace;
93
+ var method = function(event) {
94
+ var val = config.getVal.call(self, $el, event, config);
95
+ // Don't update the model if false is returned from the `updateModel` configuration.
96
+ if (evaluateBoolean(self, config.updateModel, val, config))
97
+ setAttr(model, modelAttr, val, options, self, config);
98
+ };
99
+ if (selector === '') self.$el.on(event, method);
100
+ else self.$el.on(event, selector, method);
101
+ });
102
+
103
+ // Setup a `change:modelAttr` observer to keep the view element in sync.
104
+ // `modelAttr` may be an array of attributes or a single string value.
105
+ _.each(_.flatten([modelAttr]), function(attr) {
106
+ observeModelEvent(model, self, 'change:'+attr, function(model, val, options) {
107
+ if (options == null || options.bindKey != bindKey)
108
+ updateViewBindEl(self, $el, config, getAttr(model, modelAttr, config, self), model);
109
+ });
110
+ });
111
+
112
+ updateViewBindEl(self, $el, config, getAttr(model, modelAttr, config, self), model, true);
113
+ }
114
+
115
+ // After each binding is setup, call the `initialize` callback.
116
+ applyViewFn(self, config.initialize, $el, model, config);
117
+ });
118
+
119
+ // Wrap `view.remove` to unbind stickit model and dom events.
120
+ this.remove = _.wrap(this.remove, function(oldRemove) {
121
+ self.unstickit();
122
+ if (oldRemove) oldRemove.call(self);
123
+ });
124
+ }
125
+ });
126
+
127
+ // Helpers
128
+ // -------
129
+
130
+ // Evaluates the given `path` (in object/dot-notation) relative to the given
131
+ // `obj`. If the path is null/undefined, then the given `obj` is returned.
132
+ var evaluatePath = function(obj, path) {
133
+ var parts = (path || '').split('.');
134
+ var result = _.reduce(parts, function(memo, i) { return memo[i]; }, obj);
135
+ return result == null ? obj : result;
136
+ };
137
+
138
+ // If the given `fn` is a string, then view[fn] is called, otherwise it is
139
+ // a function that should be executed.
140
+ var applyViewFn = function(view, fn) {
141
+ if (fn) return (_.isString(fn) ? view[fn] : fn).apply(view, _.toArray(arguments).slice(2));
142
+ };
143
+
144
+ var getSelectedOption = function($select) { return $select.find('option').not(function(){ return !this.selected; }); };
145
+
146
+ // Given a function, string (view function reference), or a boolean
147
+ // value, returns the truthy result. Any other types evaluate as false.
148
+ var evaluateBoolean = function(view, reference) {
149
+ if (_.isBoolean(reference)) return reference;
150
+ else if (_.isFunction(reference) || _.isString(reference))
151
+ return applyViewFn.apply(this, _.toArray(arguments));
152
+ return false;
153
+ };
154
+
155
+ // Setup a model event binding with the given function, and track the event
156
+ // in the view's _modelBindings.
157
+ var observeModelEvent = function(model, view, event, fn) {
158
+ model.on(event, fn, view);
159
+ view._modelBindings.push({model:model, event:event, fn:fn});
160
+ };
161
+
162
+ // Prepares the given `val`ue and sets it into the `model`.
163
+ var setAttr = function(model, attr, val, options, context, config) {
164
+ if (config.onSet) val = applyViewFn(context, config.onSet, val, config);
165
+ model.set(attr, val, options);
166
+ };
167
+
168
+ // Returns the given `attr`'s value from the `model`, escaping and
169
+ // formatting if necessary. If `attr` is an array, then an array of
170
+ // respective values will be returned.
171
+ var getAttr = function(model, attr, config, context) {
172
+ var val, retrieveVal = function(field) {
173
+ var retrieved = config.escape ? model.escape(field) : model.get(field);
174
+ return _.isUndefined(retrieved) ? '' : retrieved;
175
+ };
176
+ val = _.isArray(attr) ? _.map(attr, retrieveVal) : retrieveVal(attr);
177
+ return config.onGet ? applyViewFn(context, config.onGet, val, config) : val;
178
+ };
179
+
180
+ // Find handlers in `Backbone.Stickit._handlers` with selectors that match
181
+ // `$el` and generate a configuration by mixing them in the order that they
182
+ // were found with the with the givne `binding`.
183
+ var getConfiguration = function($el, binding) {
184
+ var handlers = [{
185
+ updateModel: false,
186
+ updateView: true,
187
+ updateMethod: 'text',
188
+ update: function($el, val, m, opts) { $el[opts.updateMethod](val); },
189
+ getVal: function($el, e, opts) { return $el[opts.updateMethod](); }
190
+ }];
191
+ _.each(Backbone.Stickit._handlers, function(handler) {
192
+ if ($el.is(handler.selector)) handlers.push(handler);
193
+ });
194
+ handlers.push(binding);
195
+ var config = _.extend.apply(_, handlers);
196
+ delete config.selector;
197
+ return config;
198
+ };
199
+
200
+ // Setup the attributes configuration - a list that maps an attribute or
201
+ // property `name`, to an `observe`d model attribute, using an optional
202
+ // `onGet` formatter.
203
+ //
204
+ // attributes: [{
205
+ // name: 'attributeOrPropertyName',
206
+ // observe: 'modelAttrName'
207
+ // onGet: function(modelAttrVal, modelAttrName) { ... }
208
+ // }, ...]
209
+ //
210
+ var initializeAttributes = function(view, $el, config, model, modelAttr) {
211
+ var props = ['autofocus', 'autoplay', 'async', 'checked', 'controls', 'defer', 'disabled', 'hidden', 'loop', 'multiple', 'open', 'readonly', 'required', 'scoped', 'selected'];
212
+
213
+ _.each(config.attributes || [], function(attrConfig) {
214
+ var lastClass = '',
215
+ observed = attrConfig.observe || (attrConfig.observe = modelAttr),
216
+ updateAttr = function() {
217
+ var updateType = _.indexOf(props, attrConfig.name, true) > -1 ? 'prop' : 'attr',
218
+ val = getAttr(model, observed, attrConfig, view);
219
+ // If it is a class then we need to remove the last value and add the new.
220
+ if (attrConfig.name == 'class') {
221
+ $el.removeClass(lastClass).addClass(val);
222
+ lastClass = val;
223
+ }
224
+ else $el[updateType](attrConfig.name, val);
225
+ };
226
+ _.each(_.flatten([observed]), function(attr) {
227
+ observeModelEvent(model, view, 'change:' + attr, updateAttr);
228
+ });
229
+ updateAttr();
230
+ });
231
+ };
232
+
233
+ // If `visible` is configured, then the view element will be shown/hidden
234
+ // based on the truthiness of the modelattr's value or the result of the
235
+ // given callback. If a `visibleFn` is also supplied, then that callback
236
+ // will be executed to manually handle showing/hiding the view element.
237
+ //
238
+ // observe: 'isRight',
239
+ // visible: true, // or function(val, options) {}
240
+ // visibleFn: function($el, isVisible, options) {} // optional handler
241
+ //
242
+ var initializeVisible = function(view, $el, config, model, modelAttr) {
243
+ if (config.visible == null) return;
244
+ var visibleCb = function() {
245
+ var visible = config.visible,
246
+ visibleFn = config.visibleFn,
247
+ val = getAttr(model, modelAttr, config, view),
248
+ isVisible = !!val;
249
+ // If `visible` is a function then it should return a boolean result to show/hide.
250
+ if (_.isFunction(visible) || _.isString(visible)) isVisible = applyViewFn(view, visible, val, config);
251
+ // Either use the custom `visibleFn`, if provided, or execute the standard show/hide.
252
+ if (visibleFn) applyViewFn(view, visibleFn, $el, isVisible, config);
253
+ else {
254
+ if (isVisible) $el.show();
255
+ else $el.hide();
256
+ }
257
+ };
258
+ _.each(_.flatten([modelAttr]), function(attr) {
259
+ observeModelEvent(model, view, 'change:' + attr, visibleCb);
260
+ });
261
+ visibleCb();
262
+ };
263
+
264
+ // Update the value of `$el` using the given configuration and trigger the
265
+ // `afterUpdate` callback. This action may be blocked by `config.updateView`.
266
+ //
267
+ // update: function($el, val, model, options) {}, // handler for updating
268
+ // updateView: true, // defaults to true
269
+ // afterUpdate: function($el, val, options) {} // optional callback
270
+ //
271
+ var updateViewBindEl = function(view, $el, config, val, model, isInitializing) {
272
+ if (!evaluateBoolean(view, config.updateView, val, config)) return;
273
+ config.update.call(view, $el, val, model, config);
274
+ if (!isInitializing) applyViewFn(view, config.afterUpdate, $el, val, config);
275
+ };
276
+
277
+ // Default Handlers
278
+ // ----------------
279
+
280
+ Backbone.Stickit.addHandler([{
281
+ selector: '[contenteditable="true"]',
282
+ updateMethod: 'html',
283
+ events: ['keyup', 'change', 'paste', 'cut']
284
+ }, {
285
+ selector: 'input',
286
+ events: ['keyup', 'change', 'paste', 'cut'],
287
+ update: function($el, val) { $el.val(val); },
288
+ getVal: function($el) {
289
+ var val = $el.val();
290
+ if ($el.is('[type="number"]')) return val == null ? val : Number(val);
291
+ else return val;
292
+ }
293
+ }, {
294
+ selector: 'textarea',
295
+ events: ['keyup', 'change', 'paste', 'cut'],
296
+ update: function($el, val) { $el.val(val); },
297
+ getVal: function($el) { return $el.val(); }
298
+ }, {
299
+ selector: 'input[type="radio"]',
300
+ events: ['change'],
301
+ update: function($el, val) {
302
+ $el.filter('[value="'+val+'"]').prop('checked', true);
303
+ },
304
+ getVal: function($el) {
305
+ return $el.filter(':checked').val();
306
+ }
307
+ }, {
308
+ selector: 'input[type="checkbox"]',
309
+ events: ['change'],
310
+ update: function($el, val, model, options) {
311
+ if ($el.length > 1) {
312
+ // There are multiple checkboxes so we need to go through them and check
313
+ // any that have value attributes that match what's in the array of `val`s.
314
+ val || (val = []);
315
+ _.each($el, function(el) {
316
+ if (_.indexOf(val, $(el).val()) > -1) $(el).prop('checked', true);
317
+ else $(el).prop('checked', false);
318
+ });
319
+ } else {
320
+ if (_.isBoolean(val)) $el.prop('checked', val);
321
+ else $el.prop('checked', val == $el.val());
322
+ }
323
+ },
324
+ getVal: function($el) {
325
+ var val;
326
+ if ($el.length > 1) {
327
+ val = _.reduce($el, function(memo, el) {
328
+ if ($(el).prop('checked')) memo.push($(el).val());
329
+ return memo;
330
+ }, []);
331
+ } else {
332
+ val = $el.prop('checked');
333
+ // If the checkbox has a value attribute defined, then
334
+ // use that value. Most browsers use "on" as a default.
335
+ var boxval = $el.val();
336
+ if (boxval != 'on' && boxval != null) {
337
+ if (val) val = $el.val();
338
+ else val = null;
339
+ }
340
+ }
341
+ return val;
342
+ }
343
+ }, {
344
+ selector: 'select',
345
+ events: ['change'],
346
+ update: function($el, val, model, options) {
347
+ var optList,
348
+ selectConfig = options.selectOptions,
349
+ list = selectConfig && selectConfig.collection || undefined,
350
+ isMultiple = $el.prop('multiple');
351
+
352
+ // If there are no `selectOptions` then we assume that the `<select>`
353
+ // is pre-rendered and that we need to generate the collection.
354
+ if (!selectConfig) {
355
+ selectConfig = {};
356
+ var getList = function($el) {
357
+ return $el.find('option').map(function() {
358
+ return {value:this.value, label:this.text};
359
+ }).get();
360
+ };
361
+ if ($el.find('optgroup').length) {
362
+ list = {opt_labels:[]};
363
+ _.each($el.find('optgroup'), function(el) {
364
+ var label = $(el).attr('label');
365
+ list.opt_labels.push(label);
366
+ list[label] = getList($(el));
367
+ });
368
+ } else {
369
+ list = getList($el);
370
+ }
371
+ }
372
+
373
+ // Fill in default label and path values.
374
+ selectConfig.valuePath = selectConfig.valuePath || 'value';
375
+ selectConfig.labelPath = selectConfig.labelPath || 'label';
376
+
377
+ var addSelectOptions = function(optList, $el, fieldVal) {
378
+ // Add a flag for default option at the beginning of the list.
379
+ if (selectConfig.defaultOption) {
380
+ optList = _.clone(optList);
381
+ optList.unshift('__default__');
382
+ }
383
+ _.each(optList, function(obj) {
384
+ var option = $('<option/>'), optionVal = obj;
385
+
386
+ var fillOption = function(text, val) {
387
+ option.text(text);
388
+ optionVal = val;
389
+ // Save the option value as data so that we can reference it later.
390
+ option.data('stickit_bind_val', optionVal);
391
+ if (!_.isArray(optionVal) && !_.isObject(optionVal)) option.val(optionVal);
392
+ };
393
+
394
+ if (obj === '__default__')
395
+ fillOption(selectConfig.defaultOption.label, selectConfig.defaultOption.value);
396
+ else
397
+ fillOption(evaluatePath(obj, selectConfig.labelPath), evaluatePath(obj, selectConfig.valuePath));
398
+
399
+ // Determine if this option is selected.
400
+ if (!isMultiple && optionVal != null && fieldVal != null && optionVal == fieldVal || (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)))
401
+ option.prop('selected', true);
402
+ else if (isMultiple && _.isArray(fieldVal)) {
403
+ _.each(fieldVal, function(val) {
404
+ if (_.isObject(val)) val = evaluatePath(val, selectConfig.valuePath);
405
+ if (val == optionVal || (_.isObject(val) && _.isEqual(optionVal, val)))
406
+ option.prop('selected', true);
407
+ });
408
+ }
409
+
410
+ $el.append(option);
411
+ });
412
+ };
413
+
414
+ $el.html('');
415
+
416
+ // The `list` configuration is a function that returns the options list or a string
417
+ // which represents the path to the list relative to `window` or the view/`this`.
418
+ var evaluate = function(view, list) {
419
+ var context = window;
420
+ if (list.indexOf('this.') === 0) context = view;
421
+ list = list.replace(/^[a-z]*\.(.+)$/, '$1');
422
+ return evaluatePath(context, list);
423
+ };
424
+ if (_.isString(list)) optList = evaluate(this, list);
425
+ else if (_.isFunction(list)) optList = applyViewFn(this, list, $el, options);
426
+ else optList = list;
427
+
428
+ // Support Backbone.Collection and deserialize.
429
+ if (optList instanceof Backbone.Collection) optList = optList.toJSON();
430
+
431
+ if (_.isArray(optList)) {
432
+ addSelectOptions(optList, $el, val);
433
+ } else {
434
+ // If the optList is an object, then it should be used to define an optgroup. An
435
+ // optgroup object configuration looks like the following:
436
+ //
437
+ // {
438
+ // 'opt_labels': ['Looney Tunes', 'Three Stooges'],
439
+ // 'Looney Tunes': [{id: 1, name: 'Bugs Bunny'}, {id: 2, name: 'Donald Duck'}],
440
+ // 'Three Stooges': [{id: 3, name : 'moe'}, {id: 4, name : 'larry'}, {id: 5, name : 'curly'}]
441
+ // }
442
+ //
443
+ _.each(optList.opt_labels, function(label) {
444
+ var $group = $('<optgroup/>').attr('label', label);
445
+ addSelectOptions(optList[label], $group, val);
446
+ $el.append($group);
447
+ });
448
+ }
449
+ },
450
+ getVal: function($el) {
451
+ var val;
452
+ if ($el.prop('multiple')) {
453
+ val = $(getSelectedOption($el).map(function() {
454
+ return $(this).data('stickit_bind_val');
455
+ })).get();
456
+ } else {
457
+ val = getSelectedOption($el).data('stickit_bind_val');
458
+ }
459
+ return val;
460
+ }
461
+ }]);
462
+
463
+ })(window.jQuery || window.Zepto);
@@ -0,0 +1 @@
1
+ //= require js_stack/backbone.stickit/backbone.stickit-0.6.3