From 5a0584a6ba842dd08d63ec2f5ae7688fb667b4c3 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 26 Oct 2011 09:02:10 -0500 Subject: [PATCH] moving events handlers from the Files (now Models) to the Controllers --- media/jetpack/js/Package.js | 136 +----------------- .../editor/controllers/PackageController.js | 120 ++++++++++++++-- .../js/editor/controllers/TabsController.js | 67 ++++++++- media/jetpack/js/editor/models/Attachment.js | 24 +--- media/jetpack/js/editor/models/File.js | 41 ++++++ media/jetpack/js/editor/models/Module.js | 10 +- .../js/editor/tests/controllers/package.js | 1 + .../js/editor/tests/models/attachment.js | 32 +++++ .../jetpack/js/editor/tests/models/module.js | 19 +++ media/jetpack/js/editor/views/FDEditor.js | 2 + media/jetpack/js/editor/views/Sidebar.js | 11 +- media/jetpack/js/editor/views/Tabs.js | 2 + 12 files changed, 279 insertions(+), 186 deletions(-) create mode 100644 media/jetpack/js/editor/models/File.js create mode 100644 media/jetpack/js/editor/tests/models/attachment.js create mode 100644 media/jetpack/js/editor/tests/models/module.js diff --git a/media/jetpack/js/Package.js b/media/jetpack/js/Package.js index b754ee1f..61ed0e33 100644 --- a/media/jetpack/js/Package.js +++ b/media/jetpack/js/Package.js @@ -30,9 +30,6 @@ var File = new Class({ }, destroy: function() { - // refactor me - if (this.textarea) this.textarea.destroy(); - //delete fd.editor_contents[this.get_editor_id()]; if (this.active) { // switch editor! mod = null; @@ -41,7 +38,6 @@ var File = new Class({ Object.each(this.pack.modules, function(mod) { if (!first) { first = true; - mod.switchTo(); editor.sidebar.setSelectedFile(mod); } }); @@ -57,37 +53,6 @@ var File = new Class({ this.fireEvent('destroy'); }, - onSelect: function() { - this.fireEvent('select'); - }, - - switchTo: function() { - this.selectTab(); - this.pack.editor.switchTo(this); - this.pack.editor.focus(); - this.fireEvent('showEditor'); - }, - - makeTab: function() { - var tab = this.tab = new (require('editor/views/Tabs').Tab)(editor.tabs.tabs, { - title: this.getShortName() - }); - this.addEvent('change', function() { - $(tab).addClass('modified'); - }); - this.addEvent('reset', function() { - $(tab).removeClass('modified'); - }) - tab.file = this; - }, - - selectTab: function() { - if(!this.tab) { - this.makeTab(); - } - editor.tabs.tabs.setSelected(this.tab); - }, - setChanged: function(isChanged) { if (this.changed != isChanged) { this.fireEvent(isChanged ? 'change' : 'reset'); @@ -116,20 +81,6 @@ var Library = new Class({ this.addEvent('destroy', function(){ delete pack.libraries[this.options.id_number]; }); - - if(this.options.append) { - this.append(); - } - }, - - append: function() { - editor.sidebar.addPlugin(this); - }, - - onSelect: function() { - this.parent(); - //open in a new tab, of course - window.open(this.options.view_url); }, getID: function() { @@ -179,56 +130,14 @@ var Attachment = new Class({ // uid for editor items this.uid = this.getEditorID(); - if (this.options.append) { - this.append(); - } this.addEvent('destroy', function(){ delete pack.attachments[this.options.uid]; }); // create editor - if (this.options.active && this.is_editable()) { - this.switchTo(); - } else { - pack.editor.registerItem(this); - } - }, - - onSelect: function() { - this.parent(); - if (this.is_editable()) { - this.switchTo(); - } else { - var template_start = '

' - +this.options.filename+'

'; - var template_end = '
  • ' - +'' - +'
'; - var template_middle = 'Download ' - +this.options.filename - +''; - if (this.is_image()) { - template_middle += '

'; - var img = new Element('img', { src: this.options.get_url }); - var spinner; - img.addEvent('load', function() { - if (spinner) spinner.destroy(); - modal.position(); - }); - } - var modal = this.attachmentWindow = fd.displayModal(template_start+template_middle+template_end); - var target = $(this.attachmentWindow).getElement('.UI_Modal_Section p'); - if (target) { - spinner = new Spinner(target); - spinner.show(); - target.grab(img); - } - } + pack.editor.registerItem(this); }, loadContent: function() { - // load data synchronously var that = this, spinnerEl = $(this.tab); new Request({ @@ -263,10 +172,6 @@ var Attachment = new Class({ return this.options.uid + this.options.code_editor_suffix; }, - append: function() { - editor.sidebar.addData(this); - }, - reassign: function(options) { // every revision, attachments that have changed get a new `uid`. // since Attachments are currently kept track of via the `uid`, @@ -336,29 +241,13 @@ var Module = new Class({ this.addEvent('destroy', function(){ delete pack.modules[this.options.filename]; }); - - if (this.options.append) { - this.append(); - } + // an uid for the editor this.uid = this.options.filename + this.options.suffix; // create editor - if (this.options.main || this.options.active) { - this.switchTo(); - } else { - pack.editor.registerItem(this); - } + pack.editor.registerItem(this); }, - onSelect: function() { - this.parent(); - this.switchTo(); - }, - - append: function() { - editor.sidebar.addLib(this); - }, - loadContent: function() { // load data synchronously var spinnerEl = $(this.tab); @@ -412,25 +301,8 @@ var Folder = new Class({ this.addEvent('destroy', function(){ delete pack.folders[this.options.root_dir + '/' +this.options.name]; }); - - if (this.options.append) { - this.append(); - } }, - - append: function() { - if (this.options.root_dir == Folder.ROOT_DIR_LIB) { - editor.sidebar.addLib(this); - } else if (this.options.root_dir == Folder.ROOT_DIR_DATA) { - editor.sidebar.addData(this); - } - }, - - onSelect: function() { - this.parent(); - $log('selected a Folder'); - }, - + getFullName: function() { return this.options.name; }, diff --git a/media/jetpack/js/editor/controllers/PackageController.js b/media/jetpack/js/editor/controllers/PackageController.js index 7db61751..9d25f3a2 100644 --- a/media/jetpack/js/editor/controllers/PackageController.js +++ b/media/jetpack/js/editor/controllers/PackageController.js @@ -163,18 +163,29 @@ module.exports = new Class({ // when typing in Save popover, you should be able to tab in a // logical order - this.save_el.addEvent('mouseenter', function(e) { - controller.versionEl.focus(); - }); + //TODO: this is using MooTools mouseenter + if (this.save_el.node.addEvent) { + this.save_el.node.addEvent('mouseenter', function(e) { + controller.versionEl.focus(); + }); + } else { + this.save_el.addEvent('mouseenter', function(e) { + controller.versionEl.focus(); + }) + } this.revision_message_el = dom.$('revision_message'); this.revision_message_el.addEvent('keypress', function(e) { - if (e.key == 'tab') { + //TODO: the dom Event system should make this possible + //if (e.key == 'tab') { + if (e.keyCode = 9) { e.preventDefault(); controller.save_el.focus(); } }); this.attachEditor(); + this.attachSidebar(); + this.attachTabs(); if (dom.$('jetpack_core_sdk_version')) { @@ -211,8 +222,9 @@ module.exports = new Class({ // iterate by modules and instantiate Module this.options.modules.forEach(function(module) { module.readonly = this.options.readonly; - module.append = true; - this.modules[module.filename] = new Module(this,module); + var mod = this.modules[module.filename] = new Module(this,module); + this.sidebar.addLib(mod); + if (module.main) this.editFile(mod); }, this); }, @@ -220,8 +232,8 @@ module.exports = new Class({ // iterate through attachments this.options.attachments.forEach(function(attachment) { attachment.readonly = this.options.readonly; - attachment.append = true; - this.attachments[attachment.uid] = new Attachment(this,attachment); + var att = this.attachments[attachment.uid] = new Attachment(this,attachment); + this.sidebar.addData(att); }, this); }, @@ -229,15 +241,21 @@ module.exports = new Class({ // iterate through attachments this.options.dependencies.forEach(function(plugin) { plugin.readonly = this.options.readonly; - plugin.append = true; - this.libraries[plugin.id_number] = new Library(this,plugin); + var lib = this.libraries[plugin.id_number] = new Library(this,plugin); + this.sidebar.addPlugin(lib); }, this); }, instantiate_folders: function() { this.options.folders.forEach(function(folder) { folder.append = true; - this.folders[folder.root_dir + '/' + folder.name] = new Folder(this, folder); + var key = folder.root_dir + '/' + folder.name; + var f = this.folders[key] = new Folder(this, folder); + if (folder.root_dir == Folder.ROOT_DIR_LIB) { + this.sidebar.addLib(f); + } else if (folder.root_dir == Folder.ROOT_DIR_DATA) { + this.sidebar.addData(f); + } }, this); }, @@ -443,6 +461,7 @@ module.exports = new Class({ //Package.Edit attachEditor: function() { var controller = this; + if(!this.editor) return; this.editor.addEvent('change', function() { controller.onChanged(); @@ -452,6 +471,81 @@ module.exports = new Class({ this.addEvent('save', this.onSaved); this.addEvent('reset', this.onReset); }, + + attachSidebar: function() { + if (!this.sidebar) return; + + var controller = this; + this.sidebar.addEvent('select', function(file) { + controller.onSidebarSelect(file); + }); + }, + + attachTabs: function() { + if (!this.tabs) return; + + var controller = this; + this.tabs.addEvent('select', function(tab) { + controller.onTabSelect(tab); + }) + }, + + onSidebarSelect: function(file) { + // if file is Module or Editable Attachment + if ((file instanceof Module) || + (file instanceof Attachment && file.isEditable())) { + // then open tab and editor + this.editFile(file); + } + // else if uneditable Attachment + else if (file instanceof Attachment) { + // then show in fd.modal + this.showAttachmentModal(file); + } + // else if Library + else if (file instanceof Library) { + // then open link in new window + window.open(file.options.view_url); + } + }, + + onTabSelect: function(tab) { + this.editFile(tab.file); + }, + + editFile: function(file) { + this.tabs.selectTab(file); + this.editor.switchTo(file).focus(); + }, + + showAttachmentModal: function(file) { + var template_start = '

' + +file.options.filename+'

'; + var template_end = '
  • ' + +'' + +'
'; + var template_middle = 'Download ' + +file.options.filename + +''; + if (file.is_image()) { + template_middle += '

'; + var img = new Element('img', { src: file.options.get_url }); + var spinner; + img.addEvent('load', function() { + if (spinner) spinner.destroy(); + modal.position(); + }); + } + var modal = fd.displayModal(template_start+template_middle+template_end); + var target = $(modal).getElement('.UI_Modal_Section p'); + if (target) { + spinner = new Spinner(target); + spinner.show(); + target.grab(img); + } + }, onChanged: function() { $log('FD: INFO: document changed - onbeforeunload warning is on and save button is lit.'); @@ -1344,11 +1438,9 @@ module.exports = new Class({ }, alertUnsavedData: function(e) { - if (this.edited && fd.saving) { + if (this.edited && !fd.saving) { e.preventDefault(); - e.returnValue = "You've got unsaved changes."; } - } }); diff --git a/media/jetpack/js/editor/controllers/TabsController.js b/media/jetpack/js/editor/controllers/TabsController.js index c31297b1..9cec22bc 100644 --- a/media/jetpack/js/editor/controllers/TabsController.js +++ b/media/jetpack/js/editor/controllers/TabsController.js @@ -10,19 +10,24 @@ var Class = require('shipyard/class/Class'), module.exports = new Class({ + Implements: Events, + + $tabs: [], + initialize: function() { + var controller = this; this.tabs = new tabs.TabBar('editor-tabs', { arrows: false, onTabDown: function(tab) { if (!tab.hasClass('selected')) { - tab.retrieve('tab:instance').file.onSelect(); + controller.fireEvent('select', tab.retrieve('tab:instance')); } }, onCloseDown: function(tabClose) { var tabEl = tabClose.getParent('.tab'); var nextTab = tabEl.hasClass('selected') ? - tabEl.getPrevious('.tab.') || tabEl.getNext('.tab') : - $(tabs).getElement('.tab.selected'); + tabEl.getPrevious('.tab') || tabEl.getNext('.tab') : + $(controller.tabs).getElement('.tab.selected'); if(nextTab) { var tab = tabEl.retrieve('tab:instance'), that = this, @@ -58,7 +63,7 @@ module.exports = new Class({ file.setChanged(false); fd.item.edited--; if(!fd.item.edited) { - fd.fireEvent('reset'); + fd.item.fireEvent('reset'); } }, 1); } @@ -73,6 +78,60 @@ module.exports = new Class({ } }); + }, + + addTab: function(file) { + var controller = this; + var tab = new tabs.Tab(this.tabs, { + title: file.getShortName() + }); + + function change() { + $(tab).addClass('modified'); + } + + function reset() { + $(tab).removeClass('modified'); + } + + file.addEvent('change', change); + file.addEvent('reset', reset); + + tab.addEvent('destroy', function() { + file.removeEvent('change', change); + file.removeEvent('reset', reset); + controller.removeTab(tab); + }); + tab.file = file; + + + this.$tabs.push(tab); + return tab; + }, + + removeTab: function(tab) { + //For now, simply pops the tab off the internal $tabs array + //TODO: this should probably be where all the tab destruction + //happens, instead of that massive indent-monster in the + //initialize method + var index = this.$tabs.indexOf(tab); + if (index) { + this.$tabs.splice(index, 1); + } + }, + + selectTab: function(file) { + var tab = this.getTab(file) || this.addTab(file); + this.tabs.setSelected(tab); + }, + + getTab: function(file) { + for (var i = 0, len = this.$tabs.length; i < len; i++) { + if (this.$tabs[i].file == file) { + return this.$tabs[i]; + } + } + } }); diff --git a/media/jetpack/js/editor/models/Attachment.js b/media/jetpack/js/editor/models/Attachment.js index 7fac7587..3a091013 100644 --- a/media/jetpack/js/editor/models/Attachment.js +++ b/media/jetpack/js/editor/models/Attachment.js @@ -1,16 +1,13 @@ var Class = require('shipyard/class/Class'), - Model = require('shipyard/model/Model'), fields = require('shipyard/model/fields'), - Syncable = require('shipyard/sync/Syncable'), - ServerSync = require('shipyard/sync/Server'); + ServerSync = require('shipyard/sync/Server'), + + File = require('./File'); -var EDITABLE_EXTS = ['js', 'css', 'html', 'txt', 'md', 'markdown', 'json']; module.exports = new Class({ - Extends: Model, - - Implements: Syncable, + Extends: File, Sync: { 'default': { @@ -20,19 +17,8 @@ module.exports = new Class({ }, fields: { - id: fields.NumberField(), - filename: fields.TextField({ required: true }), - ext: fields.TextField(), url: fields.TextField(), - data: fields.TextField()} - }, - - toString: function() { - return this.get('filename') + '.' + this.get('ext'); - }, - - isEditable: function() { - return EDITABLE_EXTS.indexOf(this.get('ext')) !== -1; + data: fields.TextField() } }); diff --git a/media/jetpack/js/editor/models/File.js b/media/jetpack/js/editor/models/File.js new file mode 100644 index 00000000..18deefe3 --- /dev/null +++ b/media/jetpack/js/editor/models/File.js @@ -0,0 +1,41 @@ +// Abstract model +var Class = require('shipyard/class/Class'), + Model = require('shipyard/model/Model'), + fields = require('shipyard/model/fields'), + Syncable = require('shipyard/sync/Syncable'); + +var File = module.exports = new Class({ + + Extends: Model, + + Implements: Syncable, + + fields: { + id: fields.NumberField(), + filename: fields.TextField({ required: true }), + ext: fields.TextField({ 'default': 'js' }), + content: fields.TextField() + }, + + shortName: function() { + return this.get('fullName').split('/').pop(); + }, + + fullName: function() { + var name = this.get('filename'), + ext = this.get('ext'); + + if (ext) { + return name + '.' + ext; + } else { + return name; + } + }, + + isEditable: function() { + return this.constructor.EDITABLE_EXTS.indexOf(this.get('ext')) !== -1; + } + +}); + +File.EDITABLE_EXTS = ['js', 'html', 'css', 'txt', 'json', 'md']; diff --git a/media/jetpack/js/editor/models/Module.js b/media/jetpack/js/editor/models/Module.js index a9d66dde..a8e8eca2 100644 --- a/media/jetpack/js/editor/models/Module.js +++ b/media/jetpack/js/editor/models/Module.js @@ -1,14 +1,11 @@ var Class = require('shipyard/class/Class'), - Model = require('shipyard/model/Model'), + File = require('./File'), fields = require('shipyard/model/fields'), - Syncable = require('shipyard/sync/Syncable'), ServerSync = require('shipyard/sync/Server'); module.exports = new Class({ - Extends: Model, - - Implements: Syncable, + Extends: File, Sync: { 'default': { @@ -18,9 +15,6 @@ module.exports = new Class({ }, fields: { - id: fields.NumberField(), - filename: fields.TextField(), - code: fields.TextField() } }); diff --git a/media/jetpack/js/editor/tests/controllers/package.js b/media/jetpack/js/editor/tests/controllers/package.js index b7f6ff90..f0b27cb8 100644 --- a/media/jetpack/js/editor/tests/controllers/package.js +++ b/media/jetpack/js/editor/tests/controllers/package.js @@ -218,6 +218,7 @@ module.exports = { expect(versionFocus.getCallCount()).toBe(1); var tab = new E; + tab.keyCode = 9; tab.key = 'tab'; pc.revision_message_el.fireEvent('keypress', tab); expect(saveFocus.getCallCount()).toBe(1); diff --git a/media/jetpack/js/editor/tests/models/attachment.js b/media/jetpack/js/editor/tests/models/attachment.js new file mode 100644 index 00000000..7a44c3c9 --- /dev/null +++ b/media/jetpack/js/editor/tests/models/attachment.js @@ -0,0 +1,32 @@ +var Attachment = require('../../models/Attachment'); + +module.exports = { + 'Attachment': function(it, setup) { + it('should get fullName from filename + ext', function(expect) { + var att = new Attachment({ filename: 'example.mode.html' }); + expect(att.get('fullName')).toBe('example.mode.html.js'); + + att.set('filename', 'example/mode.html'); + att.set('ext', 'txt'); + expect(att.get('fullName')).toBe('example/mode.html.txt'); + }); + + it('should be able to get shortName', function(expect) { + var att = new Attachment({ filename: 'really/long/path/file' }); + + expect(att.get('shortName')).toBe('file.js'); + }); + + it('should be editable if a text type', function(expect) { + var att = new Attachment; + expect(att.isEditable()).toBe(true); + att.set('ext', 'css'); + expect(att.isEditable()).toBe(true); + }); + + it('should not be editable if img', function(expect) { + var att = new Attachment({ ext: 'png' }); + expect(att.isEditable()).toBe(false); + }); + } +} diff --git a/media/jetpack/js/editor/tests/models/module.js b/media/jetpack/js/editor/tests/models/module.js new file mode 100644 index 00000000..13ad0996 --- /dev/null +++ b/media/jetpack/js/editor/tests/models/module.js @@ -0,0 +1,19 @@ +var Module = require('../../models/Module'); + +module.exports = { + 'Module': function(it, setup) { + + it('should be able to get fullName (filename + ext)', function(expect) { + var m = new Module({ filename: 'events/key.press' }); + expect(m.get('fullName')).toBe('events/key.press.js') + }); + + it('should be able to get shortName', function(expect) { + var m = new Module({ filename: 'main'}); + expect(m.get('shortName')).toBe('main.js'); + + m.set('filename', 'events/key.down'); + expect(m.get('shortName')).toBe('key.down.js'); + }) + } +} diff --git a/media/jetpack/js/editor/views/FDEditor.js b/media/jetpack/js/editor/views/FDEditor.js index d4b87ddb..127be472 100644 --- a/media/jetpack/js/editor/views/FDEditor.js +++ b/media/jetpack/js/editor/views/FDEditor.js @@ -99,10 +99,12 @@ var FDEditor = module.exports = new Class({ this.deactivateCurrent(); } this.activateItem(item); + return this; }, dumpCurrent: function() { this.current.content = this.getContent(); + return this; }, setReadOnly: function() { diff --git a/media/jetpack/js/editor/views/Sidebar.js b/media/jetpack/js/editor/views/Sidebar.js index 217791d3..ecd49686 100644 --- a/media/jetpack/js/editor/views/Sidebar.js +++ b/media/jetpack/js/editor/views/Sidebar.js @@ -252,12 +252,6 @@ var Sidebar = module.exports = new Class({ element.erase('file'); }); - file.addEvent('select', function() { - if (file.is_editable()) { - that.setSelectedFile(file); - } - }); - // file.onChange should add an asterisk to the tree branch // file.onReset should remove the asterisk file.addEvent('change', function() { @@ -356,9 +350,8 @@ var Sidebar = module.exports = new Class({ selectFile: function(li) { var file = li.retrieve('file'); - if(file) { - file.onSelect(); - } + this.fireEvent('select', file); + this.setSelectedFile(li); }, silentlyRemoveFolders: function(element) { diff --git a/media/jetpack/js/editor/views/Tabs.js b/media/jetpack/js/editor/views/Tabs.js index 6c75e4a4..deffe91d 100644 --- a/media/jetpack/js/editor/views/Tabs.js +++ b/media/jetpack/js/editor/views/Tabs.js @@ -53,6 +53,8 @@ var Tab = new Class({ destroy: function() { this.fireEvent('destroy'); + this.removeEvents(); + this.element = this.element.destroy(); this.close = this.close.destroy(); this.file.tab = null;