PageRenderTime 71ms CodeModel.GetById 2ms app.highlight 62ms RepoModel.GetById 2ms app.codeStats 0ms

/auiplugin/src/main/resources/experimental/js/atlassian/restfultable/restfultable.js

https://bitbucket.org/200ok/aui-archive-i18n
JavaScript | 869 lines | 533 code | 137 blank | 199 comment | 55 complexity | bdc36067364d4ca74da5501a927c0882 MD5 | raw file
  1(function ($) {
  2
  3    /**
  4     * A table who's entries/rows are can be retrieved, added and updated via rest (CRUD).
  5     * It uses backbone.js to sync the tables state back to the server and vice versa, avoiding page refreshes.
  6     *
  7     * @class RestfulTable
  8     */
  9    AJS.RestfulTable = Backbone.View.extend({
 10
 11        /**
 12         * @constructor
 13         * @param {Object} options
 14         * ... {String} id - The id for the table. This id will be used to fire events specific to this instance.
 15         * ... {boolean} allowEdit - Is the table editable. If true, clicking row will switch it to edit state.
 16         * ... {boolean} allowCreate - Can new entries be added to the table.
 17         * ... {boolean} allowReorder - Can we drag rows to reorder them.
 18         * ... {String} noEntriesMsg - Message that will be displayed under the table header if it is empty.
 19         * ... {Array} entries - initial data set to be rendered. Each item in the array will be used to create a new instance of options.model.
 20         * ... {AJS.RestfulTable.EntryModel} model - backbone model representing a row.
 21         * ... {Object} views
 22         * ... ... {AJS.RestfulTable.EditRow} editRow - Backbone view that renders the edit & create row. Your view MUST extend AJS.RestfulTable.EditRow.
 23         * ... ... {AJS.RestfulTable.Row} row - Backbone view that renders the readonly row. Your view MUST extend AJS.RestfulTable.Row.
 24         */
 25        initialize: function (options) {
 26
 27            var instance = this;
 28
 29
 30            // combine default and user options
 31            instance.options = $.extend(true, instance._getDefaultOptions(options), options);
 32
 33            // Prefix events for this instance with this id.
 34            instance.id = this.options.id;
 35
 36            // faster lookup
 37            instance._events = AJS.RestfulTable.Events;
 38            instance.classNames = AJS.RestfulTable.ClassNames;
 39            instance.dataKeys = AJS.RestfulTable.DataKeys;
 40
 41            // shortcuts to popular elements
 42            this.$table = $(options.el)
 43                    .addClass(this.classNames.RESTFUL_TABLE)
 44                    .addClass(this.classNames.ALLOW_HOVER)
 45                    .addClass("aui")
 46                    .addClass(instance.classNames.LOADING);
 47
 48            this.$table.wrapAll("<form class='aui' action='#' />");
 49            
 50            this.$thead = $("<thead/>");
 51            this.$theadRow = $("<tr />").appendTo(this.$thead);
 52            this.$tbody = $("<tbody/>");
 53
 54            if (!this.$table.length) {
 55                throw new Error("AJS.RestfulTable: Init failed! The table you have specified [" + this.$table.selector + "] cannot be found.")
 56            }
 57
 58            if (!this.options.columns) {
 59                throw new Error("AJS.RestfulTable: Init failed! You haven't provided any columns to render.")
 60            }
 61
 62            // Let user know the table is loading
 63            this.showGlobalLoading();
 64
 65             $.each(this.options.columns, function (i, column) {
 66                var header = $.isFunction(column.header) ? column.header() : column.header;
 67                if (typeof header === "undefined") {
 68                    console.warn("You have not specified [header] for column [" + column.id + "]. Using id for now...");
 69                    header = column.id;
 70                }
 71
 72                instance.$theadRow.append("<th>" + header + "</th>");
 73            });
 74
 75            // columns for submit buttons and loading indicator used when editing
 76            instance.$theadRow.append("<th></th><th></th>");
 77
 78
 79
 80            // create a new Backbone collection to represent rows (http://documentcloud.github.com/backbone/#Collection)
 81            this._models = new this.options.Collection([], {
 82                comparator: function (row) { // sort models in colleciton based on dom ordering
 83                    var index;
 84                    $.each(instance.getRows(), function (i) {
 85                        if (this.model.id === row.id) {
 86                            index = i;
 87                            return false;
 88                        }
 89                    });
 90                    return index;
 91                }
 92            });
 93
 94            // shortcut to the class we use to create rows
 95            this._rowClass = this.options.views.row;
 96
 97            if (this.options.allowEdit) {
 98                this.editRows = []; // keep track of rows that are being edited concurrently
 99                if (this.options.allowCreate !== false) {
100
101                    // Create row responsible for adding new entries ...
102                    this._createRow = new this.options.views.editRow({
103                            columns: this.options.columns,
104                            isCreateRow: true,
105                            model: this.options.model.extend({
106                                url: function () {
107                                    return instance.options.resources.self;
108                                }
109                            }),
110                            cancelAccessKey: this.options.cancelAccessKey,
111                            submitAccessKey: this.options.submitAccessKey,
112                            allowReorder: this.options.allowReorder
113                        })
114                        .bind(this._events.CREATED, function (values) {
115                            instance.addRow(values, 0);
116                        })
117                        .bind(this._events.VALIDATION_ERROR, function () {
118                            this.trigger(instance._events.FOCUS);
119                        })
120                        .render({
121                            errors: {},
122                            values: {}
123                        });
124
125                    // ... and appends it as the first row
126                    this.$create = $('<tbody class="' + this.classNames.CREATE + '" />')
127                        .append(this._createRow.el);
128
129                    // Manage which row has focus
130                    this._applyFocusCoordinator(this._createRow);
131
132                    // focus create row
133                    this._createRow.trigger(this._events.FOCUS);
134                }
135
136                this.$table.closest("form").submit(function (e) {
137                    if (instance.focusedRow) {
138                        // Delegates saving of row. See AJS.RestfulTable.EditRow.submit
139                        instance.focusedRow.trigger(instance._events.SAVE);
140                    }
141                    e.preventDefault();
142                });
143
144                if (this.options.allowReorder) {
145
146                    // Add allowance for another cell to the thead
147                    this.$theadRow.prepend("<th />");
148
149                    // Allow drag and drop reordering of rows
150                    this.$tbody.sortable({
151                        handle: "." +this.classNames.DRAG_HANDLE,
152                        helper: function(e, elt) {
153                            var helper =  elt.clone(true).addClass(instance.classNames.MOVEABLE);
154                            helper.children().each(function (i) {
155                                $(this).width(elt.children().eq(i).width());
156                            });
157                            return helper;
158                        },
159                        start: function (event, ui) {
160                            var $this = ui.placeholder.find("td");
161                            // Make sure that when we start dragging widths do not change
162                            ui.item
163                                .addClass(instance.classNames.MOVEABLE)
164                                .children().each(function (i) {
165                                    $(this).width($this.eq(i).width());
166                                });
167
168                            // Add a <td> to the placeholder <tr> to inherit CSS styles.
169                            ui.placeholder
170                                .html('<td colspan="' + instance.getColumnCount() + '">&nbsp;</td>')
171                                .css("visibility", "visible");
172
173                            // Stop hover effects etc from occuring as we move the mouse (while dragging) over other rows
174                            instance.getRowFromElement(ui.item[0]).trigger(instance._events.MODAL);
175                        },
176                        stop: function (event, ui) {
177                            ui.item
178                                .removeClass(instance.classNames.MOVEABLE)
179                                .children().attr("style", "");
180
181                            ui.placeholder.removeClass(instance.classNames.ROW);
182
183                            // Return table to a normal state
184                            instance.getRowFromElement(ui.item[0]).trigger(instance._events.MODELESS);
185                        },
186                        update: function (event, ui) {
187
188                            var nextModel,
189                                nextRow,
190                                data = {},
191                                row = instance.getRowFromElement(ui.item[0])
192
193                            if (row) {
194
195                                nextRow = ui.item.next()[0];
196
197                                if (nextRow) {
198
199                                    nextModel = instance.getRowFromElement(nextRow).model;
200
201                                    // Get the url of the for the entry befores rest endpoint.
202                                    // The server will use this to determine position.
203                                    data.after = nextModel.url();
204                                } else {
205                                    data.position = "First";
206                                }
207
208                                $.ajax({
209                                    url: row.model.url() + "/move",
210                                    type: "POST",
211                                    dataType: "json",
212                                    contentType: "application/json",
213                                    data: JSON.stringify(data),
214                                    complete: function () {
215                                        // hides loading indicator (spinner)
216                                        row.hideLoading();
217                                    },
218                                    success: function (xhr) {
219                                        AJS.triggerEvtForInst(instance._events.REORDER_SUCCESS, instance, [xhr]);
220                                    },
221                                    error: function (xhr) {
222                                        var responseData = $.parseJSON(xhr.responseText || xhr.data);
223                                        AJS.triggerEvtForInst(instance._events.SERVER_ERROR, instance, [responseData, xhr]);
224                                    }
225                                });
226
227                                // shows loading indicator (spinner)
228                                row.showLoading();
229                            }
230                        },
231                        axis: "y",
232                        delay: 0,
233                        containment: "document",
234                        cursor: "move",
235                        scroll: true,
236                        zIndex: 8000
237                    });
238
239                    // Prevent text selection while reordering.
240                    this.$tbody.bind("selectstart mousedown", function (event) {
241                        return !$(event.target).is("." + instance.classNames.DRAG_HANDLE);
242                    });
243                }
244            }
245
246            // when a model is removed from the collection, remove it from the viewport also
247            this._models.bind("remove", function (model) {
248                $.each(instance.getRows(), function (i, row) {
249                    if (row.model === model) {
250                        if (row.hasFocus() && instance._createRow) {
251                            instance._createRow.trigger(instance._events.FOCUS);
252                        }
253                        instance.removeRow(row);
254                    }
255                });
256            });
257
258            if ($.isFunction(this.options.resources.all)) {
259                this.options.resources.all(function (entries) {
260                    instance.populate(entries);
261                });
262            } else {
263                $.get(this.options.resources.all, function (entries) {
264                    instance.populate(entries);
265                });
266            }
267        },
268
269        /**
270         * Refreshes table with entries
271         *
272         * @param entries
273         */
274        populate: function (entries) {
275
276            if (this.options.reverseOrder) {
277                entries.reverse();
278            }
279
280            this.hideGlobalLoading();
281            if (entries && entries.length) {
282                // Empty the models collection
283                this._models.refresh([], { silent: true });
284                // Add all the entries to collection and render them
285                this.renderRows(entries);
286                // show message to user if we have no entries
287                if (this.isEmpty()) {
288                    this.showNoEntriesMsg();
289                }
290            } else {
291                this.showNoEntriesMsg();
292            }
293
294            // Ok, lets let everyone know that we are done...
295            this.$table
296                .append(this.$thead)
297                .append(this.$create)
298                .append(this.$tbody)
299                .removeClass(this.classNames.LOADING)
300                .trigger(this._events.INITIALIZED, [this]);
301
302            AJS.triggerEvtForInst(this._events.INITIALIZED, this, [this]);
303
304            if (this.options.autoFocus) {
305                this.$table.find(":input:text:first").focus(); // set focus to first field
306            }
307        },
308
309        /**
310         * Shows loading indicator and text
311         * 
312         * @return {AJS.RestfulTable}
313         */
314        showGlobalLoading: function () {
315
316            if (!this.$loading) {
317                 this.$loading =  $('<div class="aui-restfultable-init"><span class="aui-restfultable-throbber">' +
318                '</span><span class="aui-restfultable-loading">' + this.options.loadingMsg + '</span></div>');
319            }
320            if (!this.$loading.is(":visible")) {
321                this.$loading.insertAfter(this.$table);
322            }
323
324            return this
325        },
326
327        /**
328         * Hides loading indicator and text
329         * @return {AJS.RestfulTable}
330         */
331        hideGlobalLoading: function () {
332            if (this.$loading) {
333                this.$loading.remove();
334            }
335            return this;
336        },
337
338
339        /**
340         * Adds row to collection and renders it
341         *
342         * @param {Object} values
343         * @param {number} index
344         * @return {AJS.RestfulTable}
345         */
346        addRow: function (values, index) {
347
348            var view,
349                model;
350
351            if (!values.id) {
352                throw new Error("AJS.RestfulTable.addRow: to add a row values object must contain an id. "
353                        + "Maybe you are not returning it from your restend point?"
354                        + "Recieved:" + JSON.stringify(values));
355            }
356
357            model = new this.options.model(values);
358            view = this._renderRow(model, index);
359
360            this._models.add(model);
361            this.removeNoEntriesMsg();
362
363            // Let everyone know we added a row
364            AJS.triggerEvtForInst(this._events.ROW_ADDED, this, [view, this]);
365            return this;
366        },
367
368        /**
369         * Provided a view, removes it from display and backbone collection
370         *
371         * @param {AJS.RestfulTable.Row}
372         */
373        removeRow: function (row) {
374
375            this._models.remove(row.model);
376            row.remove();
377
378            if (this.isEmpty()) {
379                this.showNoEntriesMsg();
380            }
381
382            // Let everyone know we removed a row
383            AJS.triggerEvtForInst(this._events.ROW_REMOVED, this, [row, this]);
384        },
385
386        /**
387         * Is there any entries in the table
388         *
389         * @return {Boolean}
390         */
391        isEmpty: function () {
392            return this._models.length === 0;
393        },
394
395        /**
396         * Gets all models
397         *
398         * @return {Backbone.Collection}
399         */
400        getModels: function () {
401            return this._models;
402        },
403
404        /**
405         * Gets table body
406         *
407         * @return {jQuery}
408         */
409        getTable: function () {
410            return this.$table;
411        },
412
413        /**
414         * Gets table body
415         *
416         * @return {jQuery}
417         */
418        getTableBody: function () {
419            return this.$tbody;
420        },
421
422        /**
423         * Gets create Row
424         *
425         * @return {B
426         */
427        getCreateRow: function () {
428            return this._createRow;
429        },
430
431        /**
432         * Gets the number of table colums
433         *
434         * @return {Number}
435         */
436        getColumnCount: function () {
437            return this.options.columns.length + 2; // plus 2 accounts for the columns allocated to submit buttons and loading indicator
438        },
439
440        /**
441         * Get the AJS.RestfulTable.Row that corresponds to the given <tr> element.
442         *
443         * @param {HTMLElement} tr
444         * @return {?AJS.RestfulTable.Row}
445         */
446        getRowFromElement: function (tr) {
447            return $(tr).data(this.dataKeys.ROW_VIEW);
448        },
449
450        /**
451         * Shows message {options.noEntriesMsg} to the user if there are no entries
452         *
453         * @return {AJS.RestfulTable}
454         */
455        showNoEntriesMsg: function () {
456
457            if (this.$noEntries) {
458                this.$noEntries.remove();
459            }
460
461            this.$noEntries = $("<tr>")
462                    .addClass(this.classNames.NO_ENTRIES)
463                    .append($("<td>")
464                        .attr("colspan", this.getColumnCount())
465                        .text(this.options.noEntriesMsg)
466                    )
467                    .appendTo(this.$tbody);
468
469            return this;
470        },
471
472        /**
473         * Removes message {options.noEntriesMsg} to the user if there ARE entries
474         *
475         * @return {AJS.RestfulTable}
476         */
477        removeNoEntriesMsg: function () {
478            if (this.$noEntries && this._models.length > 0) {
479                this.$noEntries.remove();
480            }
481            return this;
482        },
483
484        /**
485         * Gets the AJS.RestfulTable.Row from their associated <tr> elements
486         *
487         * @return {Array<AJS.RestfulTable.Row>}
488         */
489        getRows: function () {
490
491            var instance = this,
492                views = [];
493
494            this.$tbody.find("." + this.classNames.READ_ONLY).each(function () {
495
496                var $row = $(this),
497                    view = $row.data(instance.dataKeys.ROW_VIEW);
498
499                if (view) {
500                    views.push(view);
501                }
502            });
503
504            return views;
505        },
506
507        /**
508         * Appends entry to end or specified index of table
509         *
510         * @param {AJS.RestfulTable.EntryModel} model
511         * @param index
512         * @return {jQuery}
513         */
514        _renderRow: function (model, index) {
515
516            var instance = this,
517                $rows = this.$tbody.find("." + this.classNames.READ_ONLY),
518                $row,
519                view;
520
521            view = new this._rowClass({
522                model: model,
523                columns: this.options.columns,
524                allowEdit: this.options.allowEdit,
525                allowReorder: this.options.allowReorder
526            });
527
528            this.removeNoEntriesMsg();
529
530            view.bind(this._events.EDIT_ROW, function (field) {
531                instance.edit(this, field);
532            });
533
534            $row = view.render().$el;
535
536            if (index !== -1) {
537
538                if (typeof index === "number" && $rows.length !== 0) {
539                    $row.insertBefore($rows[index]);
540                } else {
541                    this.$tbody.append($row);
542                }
543            }
544
545            $row.data(this.dataKeys.ROW_VIEW, view);
546
547            // deactivate all rows - used in the cases, such as opening a dropdown where you do not want the table editable
548            // or any interactions
549            view.bind(this._events.MODAL, function () {
550                instance.$table.removeClass(instance.classNames.ALLOW_HOVER);
551                instance.$tbody.sortable("disable");
552                $.each(instance.getRows(), function () {
553                    if (!instance.isRowBeingEdited(this)) {
554                        this.delegateEvents({}); // clear all events
555                    }
556                });
557            });
558
559            view.bind(this._events.ANIMATION_STARTED, function () {
560                instance.$table.removeClass(instance.classNames.ALLOW_HOVER);
561            });
562
563            view.bind(this._events.ANIMATION_FINISHED, function () {
564                instance.$table.addClass(instance.classNames.ALLOW_HOVER);
565            });
566
567            // activate all rows - used in the cases, such as opening a dropdown where you do not want the table editable
568            // or any interactions
569            view.bind(this._events.MODELESS, function () {
570                instance.$table.addClass(instance.classNames.ALLOW_HOVER);
571                instance.$tbody.sortable("enable");
572                $.each(instance.getRows(), function () {
573                    if (!instance.isRowBeingEdited(this)) {
574                        this.delegateEvents(); // rebind all events
575                    }
576                });
577            });
578
579            // ensure that when this row is focused no other are
580            this._applyFocusCoordinator(view);
581
582            this.trigger(this._events.ROW_INITIALIZED, view);
583
584            return view;
585        },
586
587        /**
588         * Returns if the row is edit mode or note
589         *
590         * @param {AJS.RestfulTable.Row} - read onyl row to check if being edited
591         * @return {Boolean}
592         */
593        isRowBeingEdited: function (row) {
594
595            var isBeingEdited = false;
596
597            $.each(this.editRows, function () {
598                if (this.el === row.el) {
599                    isBeingEdited = true;
600                    return false;
601                }
602            });
603
604            return isBeingEdited;
605        },
606
607        /**
608         * Ensures that when supplied view is focused no others are
609         *
610         * @param {Backbone.View} view
611         * @return {AJS.RestfulTable}
612         */
613        _applyFocusCoordinator: function (view) {
614
615            var instance = this;
616
617            if (!view.hasFocusBound) {
618
619                view.hasFocusBound = true;
620
621                view.bind(this._events.FOCUS, function () {
622                    if (instance.focusedRow && instance.focusedRow !== view) {
623                        instance.focusedRow.trigger(instance._events.BLUR);
624                    }
625                    instance.focusedRow = view;
626                    if (view instanceof AJS.RestfulTable.Row && instance._createRow) {
627                        instance._createRow.enable();
628                    }
629                });
630            }
631
632            return this;
633        },
634
635        /**
636         * Remove specificed row from collection holding rows being concurrently edited
637         *
638         * @param {AJS.RestfulTable.EditRow} editView
639         * @return {AJS.RestfulTable}
640         */
641        _removeEditRow: function (editView) {
642            var index = $.inArray(editView, this.editRows);
643            this.editRows.splice(index, 1);
644            return this;
645        },
646
647        /**
648         * Focuses last row still being edited or create row (if it exists)
649         *
650         * @return {AJS.RestfulTable}
651         */
652        _shiftFocusAfterEdit: function () {
653
654            if (this.editRows.length > 0) {
655                this.editRows[this.editRows.length-1].trigger(this._events.FOCUS);
656            } else if (this._createRow) {
657                this._createRow.trigger(this._events.FOCUS);
658            }
659
660            return this;
661        },
662
663        /**
664         * Evaluate if we save row when we blur. We can only do this when there is one row being edited at a time, otherwise
665         * it causes an infinate loop JRADEV-5325
666         *
667         * @return {boolean}
668         */
669        _saveEditRowOnBlur: function () {
670             return this.editRows.length <= 1;
671        },
672
673        /**
674         * Dismisses rows being edited concurrently that have no changes
675         */
676        dismissEditRows: function () {
677            var instance = this;
678            $.each(this.editRows, function () {
679                if (!this.hasUpdates()) {
680                    this.trigger(instance._events.FINISHED_EDITING);
681                }
682            });
683        },
684
685        /**
686         * Converts readonly row to editable view
687         *
688         * @param {Backbone.View} row
689         * @param {String} field - field name to focus
690         * @return {Backbone.View} editRow
691         */
692        edit: function (row, field) {
693
694            var instance = this,
695                editRow = new this.options.views.editRow({
696                    el: row.el,
697                    columns: this.options.columns,
698                    isUpdateMode: true,
699                    allowReorder: this.options.allowReorder,
700                    model: row.model,
701                    cancelAccessKey: this.options.cancelAccessKey,
702                    submitAccessKey: this.options.submitAccessKey
703                }),
704                values = row.model.toJSON();
705                values.update = true;
706                editRow.render({
707                    errors: {},
708                    update: true,
709                    values: values
710                })
711                .bind(instance._events.UPDATED, function (model, focusUpdated) {
712                    instance._removeEditRow (this);
713                    this.unbind();
714                    row.render().delegateEvents(); // render and rebind events
715                    row.trigger(instance._events.UPDATED); // trigger blur fade out
716                    if (focusUpdated !== false) {
717                        instance._shiftFocusAfterEdit();
718                    }
719                })
720                .bind(instance._events.VALIDATION_ERROR, function () {
721                    this.trigger(instance._events.FOCUS);
722                })
723                .bind(instance._events.FINISHED_EDITING, function () {
724                    instance._removeEditRow(this);
725                    row.render().delegateEvents();
726                    this.unbind();  // avoid any other updating, blurring, finished editing, cancel events being fired
727                })
728                .bind(instance._events.CANCEL, function () {
729                    instance._removeEditRow(this);
730                    this.unbind();  // avoid any other updating, blurring, finished editing, cancel events being fired
731                    row.render().delegateEvents(); // render and rebind events
732                    instance._shiftFocusAfterEdit();
733                })
734                .bind(instance._events.BLUR, function () {
735                    instance.dismissEditRows(); // dismiss edit rows that have no changes
736                    if (instance._saveEditRowOnBlur()) {
737                        this.trigger(instance._events.SAVE, false);  // save row, which if successful will call the updated event above
738                    }
739                });
740
741            // Ensure that if focus is pulled to another row, we blur the edit row
742            this._applyFocusCoordinator(editRow);
743
744            // focus edit row, which has the flow on effect of blurring current focused row
745            editRow.trigger(instance._events.FOCUS, field);
746
747            // disables form fields
748            if (instance._createRow) {
749                instance._createRow.disable();
750            }
751
752            this.editRows.push(editRow);
753
754            return editRow;
755        },
756
757
758        /**
759         * Renders all specified rows
760         *
761         * @param {Array} array of objects describing Backbone.Model's to render
762         * @return {AJS.RestfulTable}
763         */
764        renderRows: function (rows) {
765
766            var model,
767                $els = $();
768
769            // Insert prepopulated entries
770            for (var i = 0; i < rows.length; i++) {
771                model = new this.options.model(rows[i]);
772                $els = $els.add(this._renderRow(model, -1).el);
773                this._models.add(model)
774            }
775
776            this.removeNoEntriesMsg();
777
778            this.$tbody.append($els);
779
780            return this;
781        },
782
783        /**
784         * Gets default options
785         *
786         * @param {Object} options
787         */
788        _getDefaultOptions: function (options) {
789            return {
790                model: options.model || AJS.RestfulTable.EntryModel,
791                allowEdit: true,
792                views: {
793                    editRow: AJS.RestfulTable.EditRow,
794                    row: AJS.RestfulTable.Row
795                },
796                Collection: Backbone.Collection.extend({
797                    url: options.resources.self,
798                    model: options.model || AJS.RestfulTable.EntryModel
799                }),
800                allowReorder: false,
801                loadingMsg: options.loadingMsg || AJS.I18n.getText("aui.words.loading")
802            }
803        }
804
805    });
806
807    // jQuery data keys (http://api.jquery.com/jQuery.data/)
808    AJS.RestfulTable.DataKeys = {
809        ENABLED_SUBMIT: "enabledSubmit",
810        ROW_VIEW: "RestfulTable_Row_View"
811    };
812
813    // CSS style classes. DON'T hard code
814    AJS.RestfulTable.ClassNames = {
815        NO_VALUE: "aui-restfultable-editable-no-value",
816        NO_ENTRIES: "aui-restfultable-no-entires",
817        RESTFUL_TABLE: "aui-restfultable",
818        ROW: "aui-restfultable-row",
819        READ_ONLY: "aui-restfultable-readonly",
820        ACTIVE: "aui-restfultable-active",
821        ALLOW_HOVER: "aui-restfultable-allowhover",
822        FOCUSED: "aui-restfultable-focused",
823        MOVEABLE: "aui-restfultable-movable",
824        ANIMATING: "aui-restfultable-animate",
825        DISABLED: "aui-resfultable-disabled",
826        SUBMIT: "aui-restfultable-submit",
827        EDIT_ROW: "aui-restfultable-editrow",
828        CREATE: "aui-restfultable-create",
829        DRAG_HANDLE: "aui-restfultable-draghandle",
830        ORDER: "aui-restfultable-order",
831        EDITABLE: "aui-restfultable-editable",
832        ERROR: "error",
833        DELETE: "aui-resfultable-delete",
834        LOADING: "loading"
835    };
836
837    // Custom events
838    AJS.RestfulTable.Events = {
839
840        // AJS events
841        REORDER_SUCCESS: "RestfulTable.reorderSuccess",
842        ROW_ADDED: "RestfulTable.rowAdded",
843        ROW_REMOVED: "RestfulTable.rowRemoved",
844        EDIT_ROW: "RestfulTable.switchedToEditMode",
845        SERVER_ERROR: "RestfulTable.serverError",
846
847        // backbone events
848        CREATED: "created",
849        UPDATED: "updated",
850        FOCUS: "focus",
851        BLUR: "blur",
852        SUBMIT: "submit",
853        SAVE: "save",
854        MODAL: "modal",
855        MODELESS: "modeless",
856        CANCEL: "cancel",
857        CONTENT_REFRESHED: "contentRefreshed",
858        RENDER: "render",
859        FINISHED_EDITING: "finishedEditing",
860        VALIDATION_ERROR: "validationError",
861        SUBMIT_STARTED: "submitStarted",
862        SUBMIT_FINISHED: "submitFinished",
863        ANIMATION_STARTED: "animationStarted",
864        ANIMATION_FINISHED: "animationFinisehd",
865        INITIALIZED: "initialized",
866        ROW_INITIALIZED: "rowInitialized"
867    };
868
869})(AJS.$);