Initial stab at rewriting path_control.js
[armadillo.git] / web_frontend / path_control.js
1 //
2 // Armadillo File Manager
3 // Copyright (c) 2010, Robert Sesek <http://www.bluestatic.org>
4 //
5 // This program is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free Software
7 // Foundation, either version 3 of the License, or any later version.
8 //
9
10 goog.provide('armadillo.PathControl');
11
12 goog.require('goog.array');
13 goog.require('goog.ui.Control');
14 goog.require('goog.ui.FilteredMenu');
15 goog.require('goog.ui.LabelInput');
16 goog.require('goog.ui.MenuButton');
17 goog.require('goog.ui.MenuItem');
18
19 /**
20 * Creates a new path editing control for a given path.
21 * @param {string} path The path to create an editor for
22 * @param {bool} editLastComponent Whether the last component should be shown as an edit box
23 * @param {DomHelper} opt_domHelper Optional DOM helper
24 * @constructor
25 */
26 armadillo.PathControl = function(path, editLastComponent, opt_domHelper) {
27 goog.ui.Control.call(this, opt_domHelper);
28
29 /**
30 * Full path of the control.
31 * @type {string}
32 */
33 this.path_ = null;
34
35 /**
36 * The name of the file at the |path_|.
37 * @type {string}
38 */
39 this.name_ = null;
40 this.setPath(path);
41
42 /**
43 * Whether or not the last component is editable.
44 * @type {bool}
45 */
46 this.editableLastComponent_ = editLastComponent;
47
48 /**
49 * Control UI for the name component of the path.
50 * @type {goog.ui.Control}
51 */
52 this.nameControl_ = null;
53
54 /**
55 * Event Handler
56 * @type {goog.events.EventHandler}
57 */
58 this.eh_ = new goog.events.EventHandler();
59 };
60 goog.inherits(armadillo.PathControl, goog.ui.Control);
61
62 /**
63 * Disposer
64 * @protected
65 */
66 armadillo.PathControl.prototype.disposeInternal = function() {
67 armadillo.PathControl.superClass_.disposeInternal.call(this);
68 this.nameControl_ = null;
69
70 this.eh_.dispose();
71 this.eh_ = null;
72 };
73
74 /**
75 * Sets the path.
76 * @param {string} path
77 */
78 armadillo.PathControl.prototype.setPath = function(path) {
79 this.path_ = app.stripLastPathComponent(path);
80 this.name_ = path.substr(this.path_.length);
81 };
82
83 /**
84 * Gets the new path.
85 * @returns {string}
86 */
87 armadillo.PathControl.prototype.getPath = function() {
88 return app.joinPath(this.path_, this.name_);
89 };
90
91 /**
92 * Gets the name control.
93 * @returns {goog.ui.Control}
94 */
95 armadillo.PathControl.prototype.getNameControl = function() {
96 return this.nameControl_;
97 };
98
99 /**
100 * Creates a new path control object.
101 */
102 armadillo.PathControl.prototype.createDom = function() {
103 this.decorateInternal($.createDom('div'));
104 return this.element_;
105 };
106
107 /**
108 * Decorates the given element into a path control.
109 * @param {Element} element
110 */
111 armadillo.PathControl.prototype.decorateInternal = function(element) {
112 this.element_ = element;
113 var components = this.path_.split('/');
114
115 // If this is an item that lives at the root, generate a special node for
116 // moving between items at the top level.
117 components[0] = '/';
118
119 // If the last component is emtpy, do not use it because it means a directory
120 // is being moved.
121 if (components[components.length - 1] == '') {
122 goog.array.removeAt(components, components.length - 1);
123 }
124
125 var path = '';
126 $.each(components, function (i, part) {
127 this.element_.append(this.createComponentNode_(path, part), true);
128 path = app.joinPath(path, part);
129 }.bind(this));
130
131 if (this.editableLastComponent_) {
132 this.nameControl_ = $.createDom('input');
133 this.nameControl_.attr({
134 'type' : 'text',
135 'name' : 'pathName',
136 'value' : this.name_
137 });
138
139 this.nameControl_.bind('change keydown', this.nameChanged_.bind(this));
140 } else {
141 this.nameControl_ = $.createDom('span').text(this.name_);
142 }
143
144 this.element_.append(this.nameControl_);
145 };
146
147 /**
148 * @inheritDoc
149 */
150 armadillo.PathControl.prototype.enterDocument = function() {
151 armadillo.PathControl.superClass_.enterDocument.call(this);
152 this.nameControl_.getElement().focus();
153 };
154
155 /**
156 * Creates a node for a single path component.
157 * @param {string} path The path up to this point.
158 * @param {string} name The current component after |path|.
159 */
160 armadillo.PathControl.prototype.createComponentNode_ = function(path, name) {
161 var menu = new goog.ui.FilteredMenu();
162 menu.setFilterLabel(name);
163 menu.setAllowMultiple(false);
164 menu.setOpenFollowsHighlight(true);
165 goog.events.listen(menu, goog.ui.Component.EventType.ACTION,
166 this.componentChanged_, false, this);
167 this.fetchMenuContents_(path, name, menu);
168
169 var button = new goog.ui.MenuButton(name, menu, null, this.dom_);
170 button.setFocusablePopupMenu(true);
171 button.setScrollOnOverflow(true);
172 button.setVisible(true);
173 return button;
174 };
175
176 /**
177 * Queries the back-end for all the items at a given path and attaches them to
178 * the given menu.
179 * @param {string} path The path to get a list of items in
180 * @param {string} name The name to select
181 * @param {goog.ui.Menu} The menu to attach items to
182 */
183 armadillo.PathControl.prototype.fetchMenuContents_ = function(path, name, menu) {
184 var callback = function(e) {
185 var data = e.target.getResponseJson();
186 if (data['error']) {
187 app.showError(data['message']);
188 return;
189 }
190 if (path == '') {
191 // If this is the root path element, make sure the root is accessible for
192 // moving items.
193 goog.array.insertAt(data, '/', 0);
194 }
195 $.each(data, function (i, caption) {
196 // It only makes sense to be able to move into directories.
197 if (!app.isDirectory(caption)) {
198 return;
199 }
200 var item = new goog.ui.MenuItem(caption);
201 item.setValue(app.joinPath(path, name, caption));
202 menu.addItem(item);
203 if (caption == name) {
204 menu.setHighlighted(item);
205 }
206 });
207 };
208 app.sendRequest('list', {'path' : app.joinPath(path, name)}, callback);
209 };
210
211 /**
212 * Handler for changing a component of the control.
213 * @param {Event} e
214 */
215 armadillo.PathControl.prototype.componentChanged_ = function(e) {
216 this.path_ = e.target.getValue();
217 this.element_.clear();
218 this.decorateInternal(this.element_);
219 };
220
221 /**
222 * Handler for changing the editable name component.
223 * @param {Event} e
224 */
225 armadillo.PathControl.prototype.nameChanged_ = function(e) {
226 console.log(e);
227 // TODO: assert(this.editableLastComponent_)
228 this.name_ = e.target.value;
229 e.stopPropagation();
230 return true;
231 };