var $ = require('jquery'); var CoreView = require('backbone/core-view'); var template = require('./table-head-item.tpl'); var ContextMenuView = require('builder/components/context-menu/context-menu-view'); var CustomListCollection = require('builder/components/custom-list/custom-list-collection'); var TableHeadOptionsItemView = require('./table-head-options-item-view'); var tableHeadOptionsItemTemplate = require('./table-head-options-item.tpl'); var ConfirmationModalView = require('builder/components/modals/confirmation/modal-confirmation-view'); var QueryColumnModel = require('builder/data/query-column-model'); var addColumnOperation = require('builder/components/table/operations/table-add-column'); var renameColumnOperation = require('builder/components/table/operations/table-rename-column'); var changeColumnTypeOperation = require('builder/components/table/operations/table-change-column-type'); var removeTableColumnOperation = require('builder/components/table/operations/table-remove-column'); var ENTER_KEY_CODE = 13; var ESC_KEY_CODE = 27; /* * Main table view */ module.exports = CoreView.extend({ className: 'Table-headItem', tagName: 'th', events: { 'dblclick .js-attribute': '_onAttributeDblClicked', 'focusout .js-attribute': '_saveNewName', 'click .js-columnOptions': '_showContextMenu' }, initialize: function (opts) { if (!opts.tableViewModel) throw new Error('tableViewModel is required'); if (!opts.columnsCollection) throw new Error('columnsCollection is required'); if (!opts.modals) throw new Error('modals is required'); this._tableViewModel = opts.tableViewModel; this._columnsCollection = opts.columnsCollection; this._modals = opts.modals; this._hideContextMenu = this._hideContextMenu.bind(this); this._onKeyDown = this._onKeyDown.bind(this); }, render: function () { this.clearSubViews(); var columnName = this.model.get('name'); this.$el.html( template({ name: columnName, type: this.model.get('type'), isOrderBy: this._tableViewModel.get('order_by') === columnName, sortBy: this._tableViewModel.get('sort_order'), geometry: this.options.simpleGeometry }) ); return this; }, _hasContextMenu: function () { return this._menuView; }, _hideContextMenu: function () { this._unhighlightHead(); this._destroyScrollBinding(); this._menuView.collection.unbind(null, null, this); this.removeView(this._menuView); this._menuView.clean(); delete this._menuView; }, _highlightHead: function () { this.$el.addClass('is-highlighted'); }, _unhighlightHead: function () { this.$el.removeClass('is-highlighted'); }, _initScrollBinding: function () { $('.Table').scroll(this._hideContextMenu); }, _destroyScrollBinding: function () { $('.Table').off('scroll', this._hideContextMenu); }, _showContextMenu: function (ev) { var self = this; var position = { x: ev.clientX, y: ev.clientY }; var elementIndex = this.$el.index(); var modelCID = this.model.cid; var columnName = this.model.get('name'); this._highlightHead(); var menuItems = [{ label: _t('components.table.columns.options.order'), val: 'order', isOrderBy: this._tableViewModel.get('order_by') === columnName, sortBy: this._tableViewModel.get('sort_order'), action: function (model) { self._tableViewModel.set({ sort_order: model.get('sort'), order_by: columnName }); } }]; if (!this._tableViewModel.isDisabled()) { if (!this.model.isCartoDBIDColumn() && !this.model.isGeometryColumn()) { menuItems = menuItems.concat([ { label: _t('components.table.columns.options.rename'), val: 'rename', action: function () { self._startEditing(); } }, { label: _t('components.table.columns.options.change'), type: this.model.get('type'), isLastColumns: (this._columnsCollection.size() - elementIndex) < 3, val: 'change', action: function (model) { self._changeColumnType(model.get('type')); } } ]); } menuItems.push({ label: _t('components.table.columns.options.create'), val: 'create', action: function () { self._addColumn(); } }); if (!this.model.isCartoDBIDColumn() && !this.model.isGeometryColumn()) { menuItems.push( { label: _t('components.table.columns.options.delete'), val: 'delete', destructive: true, action: function () { self._removeColumn(); } } ); } } var collection = new CustomListCollection(menuItems); this._menuView = new ContextMenuView({ className: ContextMenuView.prototype.className + ' Table-columnMenu', collection: collection, itemTemplate: tableHeadOptionsItemTemplate, itemView: TableHeadOptionsItemView, triggerElementID: modelCID, position: position }); collection.bind('change:selected', function (menuItem) { var action = menuItem.get('action'); action && action(menuItem); }, this); this._menuView.model.bind('change:visible', function (model, isContextMenuVisible) { if (this._hasContextMenu() && !isContextMenuVisible) { this._hideContextMenu(); } }, this); this._menuView.show(); this.addView(this._menuView); this._initScrollBinding(); }, _addColumn: function () { addColumnOperation({ columnsCollection: this._columnsCollection }); }, _changeColumnType: function (newType) { var self = this; var oldType = this.model.get('type'); var isTypeChangeDestructive = QueryColumnModel.isTypeChangeDestructive(oldType, newType); if (!isTypeChangeDestructive) { changeColumnTypeOperation({ columnModel: this.model, newType: newType }); } else { this._modals.create( function (modalModel) { return new ConfirmationModalView({ modalModel: modalModel, template: require('./modals-templates/change-table-column-type.tpl'), renderOpts: { columnName: self.model.get('name'), newType: newType }, loadingTitle: _t('components.table.columns.change-type.loading', { columnName: self.model.get('name') }), runAction: function () { changeColumnTypeOperation({ columnModel: self.model, newType: newType, onSuccess: function () { modalModel.destroy(); }, onError: function (e) { modalModel.destroy(); } }); } }); } ); } }, _removeColumn: function () { var self = this; this._modals.create( function (modalModel) { return new ConfirmationModalView({ modalModel: modalModel, template: require('./modals-templates/remove-table-column.tpl'), renderOpts: { name: self.model.get('name') }, loadingTitle: _t('components.table.columns.destroy.loading', { columnName: self.model.get('name') }), runAction: function () { removeTableColumnOperation({ columnModel: self.model, onSuccess: function () { modalModel.destroy(); }, onError: function (e) { modalModel.destroy(); } }); } }); } ); }, _onAttributeDblClicked: function () { if (this.model.isEditable() && !this._tableViewModel.isDisabled()) { this._startEditing(); } }, _initRenameBinds: function () { $(document).bind('keydown', this._onKeyDown); }, _destroyRenameBinds: function () { $(document).unbind('keydown', this._onKeyDown); }, _onKeyDown: function (ev) { var keyCode = ev.which; if (keyCode === ENTER_KEY_CODE) { this._saveNewName(); } else if (keyCode === ESC_KEY_CODE) { this._finishEditing(); } }, _startEditing: function () { this.$('.js-attribute') .addClass('is-active') .removeAttr('readonly'); this._initRenameBinds(); }, _finishEditing: function () { this.$('.js-attribute') .val(this.model.get('name')) .removeClass('is-active') .attr('readonly', ''); this._destroyRenameBinds(); }, _saveNewName: function () { if (this.model.isEditable()) { var newName = this.$('.js-attribute').val(); var columnModel = this.model; var oldName = columnModel.get('name'); if (oldName !== newName && newName !== '') { this._modals.create( function (modalModel) { return new ConfirmationModalView({ modalModel: modalModel, template: require('./modals-templates/rename-table-column.tpl'), renderOpts: { columnName: oldName, newName: newName }, loadingTitle: _t('components.table.columns.rename.loading', { columnName: oldName, newName: newName }), runAction: function () { renameColumnOperation({ columnModel: columnModel, newName: newName, onSuccess: function () { modalModel.destroy(); }, onError: function (e) { modalModel.destroy(); } }); } }); } ); } } // Setting old name just in case user cancel it this._finishEditing(); }, focusInput: function () { this._startEditing(); this.$('.js-attribute').select(); }, clean: function () { this._destroyRenameBinds(); this._destroyScrollBinding(); CoreView.prototype.clean.apply(this); } });