rethoth 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/bin/thoth +233 -0
- data/lib/proto/config.ru +45 -0
- data/lib/proto/thoth.conf.sample +206 -0
- data/lib/thoth/cache.rb +53 -0
- data/lib/thoth/config.rb +158 -0
- data/lib/thoth/controller/admin.rb +75 -0
- data/lib/thoth/controller/api/comment.rb +73 -0
- data/lib/thoth/controller/api/page.rb +134 -0
- data/lib/thoth/controller/api/post.rb +122 -0
- data/lib/thoth/controller/api/tag.rb +59 -0
- data/lib/thoth/controller/archive.rb +50 -0
- data/lib/thoth/controller/comment.rb +173 -0
- data/lib/thoth/controller/main.rb +193 -0
- data/lib/thoth/controller/media.rb +172 -0
- data/lib/thoth/controller/page.rb +167 -0
- data/lib/thoth/controller/post.rb +310 -0
- data/lib/thoth/controller/search.rb +86 -0
- data/lib/thoth/controller/tag.rb +107 -0
- data/lib/thoth/controller.rb +48 -0
- data/lib/thoth/errors.rb +35 -0
- data/lib/thoth/helper/admin.rb +86 -0
- data/lib/thoth/helper/cookie.rb +45 -0
- data/lib/thoth/helper/error.rb +122 -0
- data/lib/thoth/helper/pagination.rb +131 -0
- data/lib/thoth/helper/wiki.rb +77 -0
- data/lib/thoth/helper/ysearch.rb +89 -0
- data/lib/thoth/importer/pants.rb +81 -0
- data/lib/thoth/importer/poseidon.rb +54 -0
- data/lib/thoth/importer/thoth.rb +103 -0
- data/lib/thoth/importer.rb +131 -0
- data/lib/thoth/layout/default.rhtml +47 -0
- data/lib/thoth/middleware/minify.rb +82 -0
- data/lib/thoth/migrate/001_create_schema.rb +108 -0
- data/lib/thoth/migrate/002_add_media_size.rb +37 -0
- data/lib/thoth/migrate/003_add_post_draft.rb +38 -0
- data/lib/thoth/migrate/004_add_comment_email.rb +37 -0
- data/lib/thoth/migrate/005_add_page_position.rb +37 -0
- data/lib/thoth/migrate/006_add_comment_close_delete.rb +43 -0
- data/lib/thoth/migrate/007_add_comment_summary.rb +37 -0
- data/lib/thoth/model/comment.rb +216 -0
- data/lib/thoth/model/media.rb +87 -0
- data/lib/thoth/model/page.rb +204 -0
- data/lib/thoth/model/post.rb +262 -0
- data/lib/thoth/model/tag.rb +80 -0
- data/lib/thoth/model/tags_posts_map.rb +34 -0
- data/lib/thoth/monkeypatch/sequel/model/errors.rb +37 -0
- data/lib/thoth/plugin/thoth_delicious.rb +105 -0
- data/lib/thoth/plugin/thoth_flickr.rb +86 -0
- data/lib/thoth/plugin/thoth_pinboard.rb +98 -0
- data/lib/thoth/plugin/thoth_tags.rb +68 -0
- data/lib/thoth/plugin/thoth_twitter.rb +175 -0
- data/lib/thoth/plugin.rb +59 -0
- data/lib/thoth/public/css/admin.css +223 -0
- data/lib/thoth/public/css/thoth.css +592 -0
- data/lib/thoth/public/images/admin-sprite.png +0 -0
- data/lib/thoth/public/images/thoth-sprite.png +0 -0
- data/lib/thoth/public/js/admin/comments.js +116 -0
- data/lib/thoth/public/js/admin/name.js +244 -0
- data/lib/thoth/public/js/admin/tagcomplete.js +332 -0
- data/lib/thoth/public/js/lazyload-min.js +4 -0
- data/lib/thoth/public/js/thoth.js +203 -0
- data/lib/thoth/public/robots.txt +5 -0
- data/lib/thoth/version.rb +37 -0
- data/lib/thoth/view/admin/index.rhtml +1 -0
- data/lib/thoth/view/admin/login.rhtml +23 -0
- data/lib/thoth/view/admin/toolbar.rhtml +117 -0
- data/lib/thoth/view/admin/welcome.rhtml +58 -0
- data/lib/thoth/view/archive/index.rhtml +24 -0
- data/lib/thoth/view/comment/comment.rhtml +47 -0
- data/lib/thoth/view/comment/delete.rhtml +15 -0
- data/lib/thoth/view/comment/form.rhtml +81 -0
- data/lib/thoth/view/comment/index.rhtml +68 -0
- data/lib/thoth/view/comment/list.rhtml +48 -0
- data/lib/thoth/view/media/delete.rhtml +15 -0
- data/lib/thoth/view/media/edit.rhtml +12 -0
- data/lib/thoth/view/media/form.rhtml +7 -0
- data/lib/thoth/view/media/list.rhtml +35 -0
- data/lib/thoth/view/media/media.rhtml +44 -0
- data/lib/thoth/view/media/new.rhtml +7 -0
- data/lib/thoth/view/page/delete.rhtml +15 -0
- data/lib/thoth/view/page/edit.rhtml +15 -0
- data/lib/thoth/view/page/form.rhtml +57 -0
- data/lib/thoth/view/page/index.rhtml +9 -0
- data/lib/thoth/view/page/list.rhtml +49 -0
- data/lib/thoth/view/page/new.rhtml +15 -0
- data/lib/thoth/view/post/comments.rhtml +12 -0
- data/lib/thoth/view/post/compact.rhtml +48 -0
- data/lib/thoth/view/post/delete.rhtml +15 -0
- data/lib/thoth/view/post/edit.rhtml +15 -0
- data/lib/thoth/view/post/form.rhtml +83 -0
- data/lib/thoth/view/post/index.rhtml +48 -0
- data/lib/thoth/view/post/list.rhtml +61 -0
- data/lib/thoth/view/post/new.rhtml +15 -0
- data/lib/thoth/view/post/tiny.rhtml +4 -0
- data/lib/thoth/view/search/index.rhtml +45 -0
- data/lib/thoth/view/tag/index.rhtml +34 -0
- data/lib/thoth/view/thoth/css.rhtml +9 -0
- data/lib/thoth/view/thoth/footer.rhtml +15 -0
- data/lib/thoth/view/thoth/header.rhtml +23 -0
- data/lib/thoth/view/thoth/index.rhtml +11 -0
- data/lib/thoth/view/thoth/js.rhtml +6 -0
- data/lib/thoth/view/thoth/sidebar.rhtml +38 -0
- data/lib/thoth/view/thoth/util/pager.rhtml +23 -0
- data/lib/thoth/view/thoth/util/simple_pager.rhtml +15 -0
- data/lib/thoth/view/thoth/util/table_sortheader.rhtml +20 -0
- data/lib/thoth.rb +394 -0
- metadata +409 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
Thoth.Admin = Thoth.Admin || {};
|
2
|
+
|
3
|
+
Thoth.Admin.Name = function () {
|
4
|
+
// Shorthand.
|
5
|
+
var d = document,
|
6
|
+
Y = YAHOO,
|
7
|
+
yut = Y.util,
|
8
|
+
yuc = yut.Connect,
|
9
|
+
yud = yut.Dom,
|
10
|
+
yue = yut.Event;
|
11
|
+
|
12
|
+
// -- Private Variables ------------------------------------------------------
|
13
|
+
var conn,
|
14
|
+
data,
|
15
|
+
delay,
|
16
|
+
originalName,
|
17
|
+
regexp = /^[0-9a-z_-]+$/i,
|
18
|
+
self;
|
19
|
+
|
20
|
+
// -- Private Methods --------------------------------------------------------
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Submits an Ajax request to determine if the current name is valid and not
|
24
|
+
* already taken.
|
25
|
+
*
|
26
|
+
* @method checkName
|
27
|
+
* @param {HTMLElement} el name input element
|
28
|
+
* @param {String} name name to check
|
29
|
+
* @private
|
30
|
+
*/
|
31
|
+
function checkName(el, name) {
|
32
|
+
if (conn && yuc.isCallInProgress(conn)) {
|
33
|
+
yuc.abort(conn);
|
34
|
+
}
|
35
|
+
|
36
|
+
var url = '/api/' + (yud.hasClass(el, 'name-post') ? 'post' : 'page') +
|
37
|
+
'/check_name?name=' + encodeURIComponent(name);
|
38
|
+
|
39
|
+
conn = yuc.asyncRequest('GET', url, {
|
40
|
+
argument: el,
|
41
|
+
success : handleCheckResponse,
|
42
|
+
timeout : 1000
|
43
|
+
});
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Hides the error message associated with the specified name input element.
|
48
|
+
*
|
49
|
+
* @method hideError
|
50
|
+
* @param {HTMLElement} el name input element
|
51
|
+
* @private
|
52
|
+
*/
|
53
|
+
function hideError(el) {
|
54
|
+
var errorEl = yud.get(el.id + '-error');
|
55
|
+
|
56
|
+
if (errorEl) {
|
57
|
+
errorEl.parentNode.removeChild(errorEl);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Shows an error message associated with the specified name input element.
|
63
|
+
*
|
64
|
+
* @method showError
|
65
|
+
* @param {HTMLElement} el name input element
|
66
|
+
* @param {String} message error message
|
67
|
+
* @private
|
68
|
+
*/
|
69
|
+
function showError(el, message) {
|
70
|
+
var errorEl = yud.get(el.id + '-error');
|
71
|
+
|
72
|
+
if (!errorEl) {
|
73
|
+
errorEl = d.createElement('p');
|
74
|
+
|
75
|
+
errorEl.id = el.id + '-error';
|
76
|
+
errorEl.className = 'flash error';
|
77
|
+
|
78
|
+
yud.insertAfter(errorEl, el);
|
79
|
+
}
|
80
|
+
|
81
|
+
errorEl.innerHTML = '';
|
82
|
+
errorEl.appendChild(d.createTextNode(message));
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Submits an Ajax request for a name suggestion based on the contents of the
|
87
|
+
* specified title input element.
|
88
|
+
*
|
89
|
+
* @method suggestName
|
90
|
+
* @param {HTMLElement} el title input element
|
91
|
+
* @param {String} title title string
|
92
|
+
* @private
|
93
|
+
*/
|
94
|
+
function suggestName(el, title) {
|
95
|
+
if (conn && yuc.isCallInProgress(conn)) {
|
96
|
+
yuc.abort(conn);
|
97
|
+
}
|
98
|
+
|
99
|
+
var url = '/api/' + (yud.hasClass(el, 'title-post') ? 'post' : 'page') +
|
100
|
+
'/suggest_name?title=' + encodeURIComponent(title);
|
101
|
+
|
102
|
+
conn = yuc.asyncRequest('GET', url, {
|
103
|
+
argument: el,
|
104
|
+
success : handleSuggestResponse,
|
105
|
+
timeout : 1000
|
106
|
+
});
|
107
|
+
}
|
108
|
+
|
109
|
+
// -- Private Event Handlers -------------------------------------------------
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Handles Ajax check_name responses.
|
113
|
+
*
|
114
|
+
* @method handleCheckResponse
|
115
|
+
* @param {Object} response response object
|
116
|
+
* @private
|
117
|
+
*/
|
118
|
+
function handleCheckResponse(response) {
|
119
|
+
var data
|
120
|
+
|
121
|
+
try {
|
122
|
+
data = Y.lang.JSON.parse(response.responseText);
|
123
|
+
} catch (e) {
|
124
|
+
return;
|
125
|
+
}
|
126
|
+
|
127
|
+
if (!data.valid) {
|
128
|
+
showError(response.argument, 'Names may only contain letters, numbers, ' +
|
129
|
+
'underscores, and dashes, and may not be entirely numeric.');
|
130
|
+
} else if (!data.unique && response.argument.value !== originalName) {
|
131
|
+
showError(response.argument, 'This name is already taken.');
|
132
|
+
} else {
|
133
|
+
hideError(response.argument);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Handles keypress events on name input fields.
|
139
|
+
*
|
140
|
+
* @method handleKeyPress
|
141
|
+
* @param {Event} e event object
|
142
|
+
* @private
|
143
|
+
*/
|
144
|
+
function handleKeyPress(e) {
|
145
|
+
var c = String.fromCharCode(yue.getCharCode(e)),
|
146
|
+
el = yue.getTarget(e);
|
147
|
+
|
148
|
+
if (e.isChar) {
|
149
|
+
if (regexp.test(c)) {
|
150
|
+
if (/[A-Z]/.test(c)) {
|
151
|
+
yue.stopEvent(e);
|
152
|
+
el.value += c.toLowerCase();
|
153
|
+
}
|
154
|
+
} else {
|
155
|
+
yue.stopEvent(e);
|
156
|
+
|
157
|
+
if (c === ' ') {
|
158
|
+
el.value += '-';
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Handles keyup events on name input fields.
|
166
|
+
*
|
167
|
+
* @method handleKeyUp
|
168
|
+
* @param {Event} e event object
|
169
|
+
* @private
|
170
|
+
*/
|
171
|
+
function handleKeyUp(e) {
|
172
|
+
var el = yue.getTarget(e);
|
173
|
+
|
174
|
+
clearTimeout(delay);
|
175
|
+
|
176
|
+
if (el.value) {
|
177
|
+
yue.removeListener('title', 'change', handleTitleChange);
|
178
|
+
|
179
|
+
delay = setTimeout(function () {
|
180
|
+
checkName(el, el.value);
|
181
|
+
}, 200);
|
182
|
+
} else {
|
183
|
+
hideError(el);
|
184
|
+
yue.on('title', 'change', handleTitleChange, self, true);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Handles Ajax suggest_name responses.
|
190
|
+
*
|
191
|
+
* @method handleSuggestResponse
|
192
|
+
* @param {Object} response response object
|
193
|
+
* @private
|
194
|
+
*/
|
195
|
+
function handleSuggestResponse(response) {
|
196
|
+
try {
|
197
|
+
yud.get('name').value = Y.lang.JSON.parse(response.responseText).name;
|
198
|
+
} catch (e) {}
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Handles change events on title input fields.
|
203
|
+
*
|
204
|
+
* @method handleTitleChange
|
205
|
+
* @param {Event} e event object
|
206
|
+
* @private
|
207
|
+
*/
|
208
|
+
function handleTitleChange(e) {
|
209
|
+
var el = yue.getTarget(e);
|
210
|
+
|
211
|
+
if (el.value) {
|
212
|
+
suggestName(el, el.value);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
return {
|
217
|
+
// -- Public Methods -------------------------------------------------------
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Initializes the Name module on this page.
|
221
|
+
*
|
222
|
+
* @method init
|
223
|
+
*/
|
224
|
+
init: function () {
|
225
|
+
var name = yud.get('name');
|
226
|
+
|
227
|
+
self = this;
|
228
|
+
|
229
|
+
// Listen for keys on name input fields.
|
230
|
+
yue.on(name, 'keypress', handleKeyPress, self, true);
|
231
|
+
yue.on(name, 'keyup', handleKeyUp, self, true);
|
232
|
+
|
233
|
+
if (!name.value.length) {
|
234
|
+
// Listen for changes to title input fields.
|
235
|
+
yue.on('title', 'change', handleTitleChange, self, true);
|
236
|
+
} else {
|
237
|
+
originalName = name.value;
|
238
|
+
}
|
239
|
+
},
|
240
|
+
};
|
241
|
+
}();
|
242
|
+
|
243
|
+
YAHOO.util.Event.onContentReady('doc', Thoth.Admin.Name.init, Thoth.Admin.Name,
|
244
|
+
true);
|
@@ -0,0 +1,332 @@
|
|
1
|
+
Thoth.Admin = Thoth.Admin || {};
|
2
|
+
|
3
|
+
Thoth.Admin.TagComplete = function () {
|
4
|
+
// Shorthand.
|
5
|
+
var d = document,
|
6
|
+
Y = YAHOO,
|
7
|
+
yut = Y.util,
|
8
|
+
yuc = yut.Connect,
|
9
|
+
yud = yut.Dom,
|
10
|
+
yue = yut.Event;
|
11
|
+
|
12
|
+
// -- Private Variables ------------------------------------------------------
|
13
|
+
var cache = {},
|
14
|
+
conn,
|
15
|
+
data;
|
16
|
+
|
17
|
+
// -- Private Methods --------------------------------------------------------
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Gets the last tag in the specified input element, or <i>null</i> if there
|
21
|
+
* are no tags.
|
22
|
+
*
|
23
|
+
* @method getLastTag
|
24
|
+
* @param {HTMLElement|String} el input element
|
25
|
+
* @return {String|null} last tag or <i>null</i> if there are no tags
|
26
|
+
* @private
|
27
|
+
*/
|
28
|
+
function getLastTag(el) {
|
29
|
+
if (tag = getTags(el).pop()) {
|
30
|
+
return tag;
|
31
|
+
}
|
32
|
+
|
33
|
+
return null;
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Returns a reference to the suggestion list element associated with the
|
38
|
+
* specified tag input element. If the specified element is a suggestion list
|
39
|
+
* element, it will be returned.
|
40
|
+
*
|
41
|
+
* @method getSuggestEl
|
42
|
+
* @param {HTMLElement|String} el input element
|
43
|
+
* @return {HTMLElement} suggestion list element or <i>null</i> if not found
|
44
|
+
* @private
|
45
|
+
*/
|
46
|
+
function getSuggestEl(el) {
|
47
|
+
el = yud.get(el);
|
48
|
+
|
49
|
+
if (yud.hasClass(el, 'suggested-tags')) {
|
50
|
+
return el;
|
51
|
+
}
|
52
|
+
|
53
|
+
return yud.getNextSiblingBy(el, function (node) {
|
54
|
+
return yud.hasClass(node, 'suggested-tags');
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Parses a comma-separated list of tags in the specified input element and
|
60
|
+
* returns it as an Array.
|
61
|
+
*
|
62
|
+
* @method getTags
|
63
|
+
* @param {HTMLElement|String} el tag input element
|
64
|
+
* @return {Array} tags
|
65
|
+
* @private
|
66
|
+
*/
|
67
|
+
function getTags(el) {
|
68
|
+
var value = (el = yud.get(el)) ? el.value : null;
|
69
|
+
|
70
|
+
if (value && value.length) {
|
71
|
+
return value.split(/,\s*/);
|
72
|
+
}
|
73
|
+
|
74
|
+
return [];
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Replaces the last tag in the specified tag input element with the specified
|
79
|
+
* tag.
|
80
|
+
*
|
81
|
+
* @method replaceLastTag
|
82
|
+
* @param {HTMLElement|String} el tag input element
|
83
|
+
* @param {String} tag tag text
|
84
|
+
* @private
|
85
|
+
*/
|
86
|
+
function replaceLastTag(el, tag) {
|
87
|
+
var tags = getTags(el = yud.get(el));
|
88
|
+
|
89
|
+
tags.pop()
|
90
|
+
tags.push(tag);
|
91
|
+
|
92
|
+
el.value = tags.join(', ');
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Sends an Ajax request for tags matching the specified query.
|
97
|
+
*
|
98
|
+
* @method requestTags
|
99
|
+
* @param {HTMLElement} el input element that triggered the request
|
100
|
+
* @param {String} query query string
|
101
|
+
* @param {Number} (optional) limit maximum number of tags to return
|
102
|
+
* @private
|
103
|
+
*/
|
104
|
+
function requestTags(el, query, limit) {
|
105
|
+
if (conn && yuc.isCallInProgress(conn)) {
|
106
|
+
yuc.abort(conn);
|
107
|
+
}
|
108
|
+
|
109
|
+
var url = '/api/tag/suggest?q=' + encodeURIComponent(query);
|
110
|
+
|
111
|
+
if (limit) {
|
112
|
+
url += '&limit=' + encodeURIComponent(limit);
|
113
|
+
}
|
114
|
+
|
115
|
+
if (cache[url]) {
|
116
|
+
handleResponse(cache[url]);
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
120
|
+
conn = yuc.asyncRequest('GET', url, {
|
121
|
+
argument: [url, el],
|
122
|
+
success : handleResponse,
|
123
|
+
timeout : 1000
|
124
|
+
});
|
125
|
+
}
|
126
|
+
|
127
|
+
// -- Private Event Handlers -------------------------------------------------
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Handles keydown events on tag input fields.
|
131
|
+
*
|
132
|
+
* @method handleKeyDown
|
133
|
+
* @param {Event} e event object
|
134
|
+
* @private
|
135
|
+
*/
|
136
|
+
function handleKeyDown(e) {
|
137
|
+
var charCode = yue.getCharCode(e),
|
138
|
+
el = yue.getTarget(e),
|
139
|
+
suggestEl;
|
140
|
+
|
141
|
+
// Autocomplete the current tag when the tab key is pressed.
|
142
|
+
if (charCode === 9 && !e.shiftKey && !e.ctrlKey && !e.altKey &&
|
143
|
+
!e.metaKey && data.length && this.isVisible(el)) {
|
144
|
+
yue.preventDefault(e);
|
145
|
+
replaceLastTag(el, data[0][0]);
|
146
|
+
this.hide(el);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
/**
|
151
|
+
* Handles keyup events on tag input fields.
|
152
|
+
*
|
153
|
+
* @method handleKeyUp
|
154
|
+
* @param {Event} e event object
|
155
|
+
* @private
|
156
|
+
*/
|
157
|
+
function handleKeyUp(e) {
|
158
|
+
var charCode = yue.getCharCode(e),
|
159
|
+
el = yue.getTarget(e),
|
160
|
+
tag;
|
161
|
+
|
162
|
+
if (charCode < 46 && charCode !== 8 && charCode !== 32) {
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
|
166
|
+
tag = getLastTag(el);
|
167
|
+
|
168
|
+
if (tag) {
|
169
|
+
requestTags(el, tag);
|
170
|
+
} else {
|
171
|
+
this.hide(el);
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Handles Ajax responses.
|
177
|
+
*
|
178
|
+
* @method handleResponse
|
179
|
+
* @param {Object} response response object
|
180
|
+
* @private
|
181
|
+
*/
|
182
|
+
function handleResponse(response) {
|
183
|
+
if (!cache[response.argument[0]]) {
|
184
|
+
cache[response.argument[0]] = response;
|
185
|
+
}
|
186
|
+
|
187
|
+
try {
|
188
|
+
data = Y.lang.JSON.parse(response.responseText);
|
189
|
+
Thoth.Admin.TagComplete.refresh(response.argument[1], data);
|
190
|
+
} catch (e) {}
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* Handles clicks on the tag suggestions.
|
195
|
+
*
|
196
|
+
* @method handleTagClick
|
197
|
+
* @param {Event} e event object
|
198
|
+
* @param {HTMLElement} el tag input element
|
199
|
+
* @private
|
200
|
+
*/
|
201
|
+
function handleTagClick(e, el) {
|
202
|
+
var a = yue.getTarget(e);
|
203
|
+
|
204
|
+
if (a.tagName.toLowerCase() !== 'a') {
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
|
208
|
+
yue.preventDefault(e);
|
209
|
+
replaceLastTag(el, a.getAttribute('tagText'));
|
210
|
+
el.focus();
|
211
|
+
|
212
|
+
Thoth.Admin.TagComplete.hide(el);
|
213
|
+
}
|
214
|
+
|
215
|
+
return {
|
216
|
+
// -- Public Methods -------------------------------------------------------
|
217
|
+
|
218
|
+
/**
|
219
|
+
* Initializes the TagComplete module on this page.
|
220
|
+
*
|
221
|
+
* @method init
|
222
|
+
*/
|
223
|
+
init: function () {
|
224
|
+
var self = this,
|
225
|
+
list;
|
226
|
+
|
227
|
+
yud.getElementsByClassName('tags-input', 'input', 'doc', function (el) {
|
228
|
+
// Listen for keys on tag input fields.
|
229
|
+
yue.on(el, 'keydown', handleKeyDown, self, true);
|
230
|
+
yue.on(el, 'keyup', handleKeyUp, self, true);
|
231
|
+
|
232
|
+
// Turn off browser autocomplete to avoid excess annoyingness.
|
233
|
+
el.setAttribute('autocomplete', 'off');
|
234
|
+
|
235
|
+
// Create a suggestion list element under the input element.
|
236
|
+
list = d.createElement('ol');
|
237
|
+
list.className = 'suggested-tags hidden';
|
238
|
+
|
239
|
+
yud.insertAfter(list, el);
|
240
|
+
yue.on(list, 'click', handleTagClick, el);
|
241
|
+
});
|
242
|
+
},
|
243
|
+
|
244
|
+
/**
|
245
|
+
* Clears the suggestions for the specified element.
|
246
|
+
*
|
247
|
+
* @method clear
|
248
|
+
* @param {HTMLElement|String} el input element or suggestion list
|
249
|
+
*/
|
250
|
+
clear: function (el) {
|
251
|
+
if (el = getSuggestEl(el)) {
|
252
|
+
el.innerHTML = '';
|
253
|
+
}
|
254
|
+
},
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Hides the suggestions for the specified element.
|
258
|
+
*
|
259
|
+
* @method hide
|
260
|
+
* @param {HTMLElement|String} el input element or suggestion list
|
261
|
+
*/
|
262
|
+
hide: function (el) {
|
263
|
+
if (el = getSuggestEl(el)) {
|
264
|
+
yud.addClass(el, 'hidden');
|
265
|
+
}
|
266
|
+
},
|
267
|
+
|
268
|
+
/**
|
269
|
+
* Returns <i>true</i> if suggestions for the specified element are visible,
|
270
|
+
* <i>false</i> otherwise.
|
271
|
+
*
|
272
|
+
* @method isVisible
|
273
|
+
* @param {HTMLElement|String} el input element or suggestion list
|
274
|
+
* @return <i>true</i> if suggestions are visible, <i>false</i> otherwise
|
275
|
+
*/
|
276
|
+
isVisible: function (el) {
|
277
|
+
return !yud.hasClass(getSuggestEl(el), 'hidden');
|
278
|
+
},
|
279
|
+
|
280
|
+
/**
|
281
|
+
* Refreshes the tag suggestions for the specified element.
|
282
|
+
*
|
283
|
+
* @method refresh
|
284
|
+
* @param {HTMLElement|String} el input element or suggestion list
|
285
|
+
* @param {Array} (optional) tags suggested tags
|
286
|
+
*/
|
287
|
+
refresh: function (el, tags) {
|
288
|
+
var a, i, li, tag;
|
289
|
+
|
290
|
+
if (!(el = getSuggestEl(el))) {
|
291
|
+
return;
|
292
|
+
}
|
293
|
+
|
294
|
+
this.clear(el);
|
295
|
+
|
296
|
+
if (!tags || tags.length === 0) {
|
297
|
+
this.hide(el);
|
298
|
+
return;
|
299
|
+
}
|
300
|
+
|
301
|
+
for (i = 0; i < tags.length; ++i) {
|
302
|
+
tag = tags[i];
|
303
|
+
li = d.createElement('li');
|
304
|
+
a = d.createElement('a');
|
305
|
+
|
306
|
+
a.href = '#';
|
307
|
+
a.appendChild(d.createTextNode(tag[0] + ' (' + tag[1] + ')'));
|
308
|
+
a.setAttribute('tagText', tag[0]);
|
309
|
+
|
310
|
+
li.appendChild(a);
|
311
|
+
el.appendChild(li);
|
312
|
+
}
|
313
|
+
|
314
|
+
this.show(el);
|
315
|
+
},
|
316
|
+
|
317
|
+
/**
|
318
|
+
* Shows the suggestions for the specified element.
|
319
|
+
*
|
320
|
+
* @method show
|
321
|
+
* @param {HTMLElement|String} el input element or suggestion list
|
322
|
+
*/
|
323
|
+
show: function (el) {
|
324
|
+
if (el = getSuggestEl(el)) {
|
325
|
+
yud.removeClass(el, 'hidden');
|
326
|
+
}
|
327
|
+
}
|
328
|
+
};
|
329
|
+
}();
|
330
|
+
|
331
|
+
YAHOO.util.Event.onContentReady('doc', Thoth.Admin.TagComplete.init,
|
332
|
+
Thoth.Admin.TagComplete, true);
|
@@ -0,0 +1,4 @@
|
|
1
|
+
LazyLoad=function(){function r(c,b){c=i.createElement(c);var a;for(a in b)b.hasOwnProperty(a)&&c.setAttribute(a,b[a]);return c}function k(c){var b=h[c],a,e;if(b){a=b.callback;e=b.urls;e.shift();l=0;if(!e.length){if(a)a.call(b.context||window,b.obj);h[c]=null;j[c].length&&m(c)}}}function w(){if(!d){var c=navigator.userAgent,b=parseFloat,a;d={gecko:0,ie:0,opera:0,webkit:0};if((a=c.match(/AppleWebKit\/(\S*)/))&&a[1])d.webkit=b(a[1]);else if((a=c.match(/MSIE\s([^;]*)/))&&a[1])d.ie=b(a[1]);else if(/Gecko\/(\S*)/.test(c)){d.gecko=
|
2
|
+
1;if((a=c.match(/rv:([^\s\)]*)/))&&a[1])d.gecko=b(a[1])}else if(a=c.match(/Opera\/(\S*)/))d.opera=b(a[1])}}function m(c,b,a,e,s){var n=function(){k(c)},o=c==="css",f,g,p;w();if(b){b=b.constructor===Array?b:[b];if(o||d.gecko&&d.gecko<2||d.opera)j[c].push({urls:[].concat(b),callback:a,obj:e,context:s});else{f=0;for(g=b.length;f<g;++f)j[c].push({urls:[b[f]],callback:f===g-1?a:null,obj:e,context:s})}}if(!(h[c]||!(p=h[c]=j[c].shift()))){q=q||i.head||i.getElementsByTagName("head")[0];b=p.urls;f=0;for(g=
|
3
|
+
b.length;f<g;++f){a=b[f];a=o?r("link",{charset:"utf-8","class":"lazyload",href:a,rel:"stylesheet",type:"text/css"}):r("script",{charset:"utf-8","class":"lazyload",src:a});if(d.ie)a.onreadystatechange=function(){var t=this.readyState;if(t==="loaded"||t==="complete"){this.onreadystatechange=null;n()}};else if(o&&(d.gecko||d.webkit))if(d.webkit){p.urls[f]=a.href;u()}else setTimeout(n,50*g);else a.onload=a.onerror=n;q.appendChild(a)}}}function u(){var c=h.css,b;if(c){for(b=v.length;--b;)if(v[b].href===
|
4
|
+
c.urls[0]){k("css");break}l+=1;if(c)l<200?setTimeout(u,50):k("css")}}var i=document,q,h={},l=0,j={css:[],js:[]},v=i.styleSheets,d;return{css:function(c,b,a,e){m("css",c,b,a,e)},js:function(c,b,a,e){m("js",c,b,a,e)}}}();
|