PageRenderTime 41ms CodeModel.GetById 2ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

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

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