cookieoflife (owner)

Fork Of

gist: 7516 by wireframe firefox ubiquity command fo...

Revisions

gist: 60673 Download_button fork
public
Public Clone URL: git://gist.github.com/60673.git
delicious-ubiquity-command.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
/**
* share-on-delicious - an Ubiquity command for sharing bookmarks on
* delicious.com
*
* l.m.orchard@pobox.com
* http://decafbad.com/
* Share and Enjoy!
*
* This version is entirely ripped up: status bar delete, non-cookie authorization included. Bon appetit!
*/
var share_on_delicious_cmd = (function() {
 
    return {
 
        name:
            'delicious',
        icon:
            'http://delicious.com/favicon.ico',
        description:
            'Share the current page as a bookmark on delicious.com',
        help: <span>
            Select text on the page to use as notes, or enter your own
            text after the command word. You can also assign tags to the
            bookmark with the <code>tagged</code> modifier, and alter the bookmark
            default page title with the <code>entitled</code> modifier. Note that
            you must also already be logged in at delicious.com to use
            this command.
        </span>.toXMLString(),
 
        homepage:
            'http://decafbad.com',
        author: {
            name: 'Leslie Michael Orchard',
            email: 'l.m.orchard@pobox.com'
        },
        license:
            'MPL/GPL/LGPL',
 
        /**
* Initialize the command package. Creates the command after doing
* a last few bits of wiring. Called at the very end of the file.
*/
        init: function() {
            this.takes = {
                quote: noun_arb_text
            };
            this.modifiers = {
                quoted: noun_arb_text,
                tagged: noun_arb_text,
                // tagged: this.noun_type_tags,
                entitled: noun_arb_text
            };
            CmdUtils.CreateCommand(this);
            return this;
        },
 
        /**
* Command configuration settings.
*/
        config: {
            // Base URL for the delicious v1 API
            api_base: 'https://api.del.icio.us',
 
            // Domain and name of the delicious login session cookie.
            cookie_domain: '.delicious.com',
            cookie_name: '_user',
 
            // ID for the XUL element displaying bookmark info
            status_bar_id: 'ubiq-delicious-panel'
        },
 
        // XML NS for XUL elements, used for the status bar panel.
        XUL_NS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
 
        // Cache of URL info loaded up from Delicious
        urlinfo: {},
 
 
        /**
* Present a preview of the bookmark under construction during the course
* of composing the command.
*/
        preview: function(pblock, input_obj, mods) {
            var bm = this.extractBookmarkData(input_obj, mods);
            var user_cookie = this.getUserCookie();
            var user_name = (user_cookie) ? user_cookie.split(' ')[0] : '';
 
            var chars_left = (bm.extended) ? 1000 - bm.extended.length : 1000;
            var ns = {
                user_name: user_name,
                bm: bm,
                chars_left: chars_left,
                chars_over: (chars_left < 0)
            };
 
            var tmpl;
            if (!bm.description) {
                tmpl = this.templates.preview_title_error;
            } else {
                tmpl = this.templates.preview_full;
            }
 
            pblock.innerHTML = this.renderE4XTemplate(tmpl, ns);
        },
        
        /**
* Attempt to use the delicious v1 API to post a bookmark using the
* command input
*/
        execute: function(input_obj, mods) {
            var bm = this.extractBookmarkData(input_obj, mods);
            var user_cookie = this.getUserCookie();
            var user_name = (user_cookie) ? user_cookie.split(' ')[0] : '';
 
            if (!bm.description) {
                displayMessage(
                    "A title is required for bookmarks at delicious.com"
                );
                return false;
            }
 
            if (bm.extended && bm.extended.length > 1000) {
                displayMessage(
                    "The bookmark notes are " +
                        Math.abs(1000-bm.extended.length) + " characters " +
                        "too long."
                );
                return false;
            }
 
            this.v1api(
                '/v1/posts/add', bm,
                function() {
                    displayMessage('Bookmark "' + bm.description + '" ' +
                        'shared at delicious.com/' + user_name);
                },
                function() {
                    // TODO: more informative reporting on errors
                    displayMessage('ERROR: Bookmark "' + bm.description + '" ' +
                        ' NOT shared on delicious.com/' + user_name);
                }
            );
 
        },
 
        /**
* A noun type for suggesting Delicious tags
*
* TODO: fetch and cache tags from user's account
* TODO: use the tag suggestion call on the API
*/
        noun_type_tags: {
 
            _name: 'tags',
 
            getTags: function() {
                return [ 'osx', 'apple', 'software', 'testing' ];
            },
 
            suggest: function(text, html) {
                var sugg_tags = this.getTags();
                var curr_tags = (''+text).split(' ');
                
                var suggestions = [];
 
                if (curr_tags.length) {
                    var last_tag = curr_tags.pop();
 
                    for (var i=0,tag; tag=sugg_tags[i]; i++) {
                        if ( tag.indexOf( last_tag ) > -1 ) {
                            var sugg_tags = curr_tags.join(' ') + ' ' + tag;
                            suggestions.push( CmdUtils.makeSugg( sugg_tags, sugg_tags, sugg_tags ) );
                        }
                    }
                }
 
                return suggestions;
            }
 
        },
 
        /**
* Fire off a Delicious V1 API request.
*/
        v1api: function(path, data, success, error) {
 
            // Build a User-Agent string derived from the browser's, if none
            // previously defined.
            if (!this.user_agent) {
                var mediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].
                    getService(Components.interfaces.nsIWindowMediator);
                var win = mediator.getMostRecentWindow(null);
                this.user_agent = win.navigator.userAgent + ";Ubiquity-share-on-delicious";
            }
 
            // Inject the user auth cookie into the API parameters.
            data['_user'] = this.getUserCookie();
            
            jQuery.ajax({
                type: 'POST',
                url: this.config.api_base + path,
                data: this.buildQueryString(data),
                beforeSend: function(req) {
                    req.setRequestHeader("User-Agent", this.user_agent);
                },
                success: success || function() {
                    displayMessage('Delicious API call ' + path + ' succeeded.');
                },
                error: error || function() {
                    displayMessage('ERROR: Delicious API call ' + path + ' failed!');
                }
            });
 
        },
 
        /**
* Given input data and modifiers, attempt to assemble data necessary to
* post a bookmark.
*/
        extractBookmarkData: function(input_obj, mods) {
            return {
                url:
                    Application.activeWindow.activeTab.uri.spec,
                description:
                    mods.entitled.text || context.focusedWindow.document.title,
                extended:
                    input_obj.text + ( mods.quoted.text ? ' "' + mods.quoted.text + '"' : '' ),
                tags:
                    mods.tagged.text
            };
        },
 
        /**
* Dig up the Delicious login session cookie.
*/
        getUserCookie: function() {
            var cookie_mgr = Components.classes["@mozilla.org/cookiemanager;1"]
                .getService(Components.interfaces.nsICookieManager);
            var iter = cookie_mgr.enumerator;
            while (iter.hasMoreElements()) {
                var cookie = iter.getNext();
                if( cookie instanceof Components.interfaces.nsICookie &&
                    cookie.host.indexOf(this.config.cookie_domain) != -1 &&
                    cookie.name == this.config.cookie_name) {
                    return decodeURIComponent(cookie.value);
                }
            }
        },
 
        /**
* Given an object, build a URL query string
*/
        buildQueryString: function(data) {
            var qs = [];
            for (k in data) if (data[k])
                qs.push( encodeURIComponent(k) + '=' +
                    encodeURIComponent(data[k]) );
            return qs.join('&');
        },
 
        /**
* Calculate an MD5 hash for a given string
*/
        md5: function(str) {
            var Ci = Components.interfaces;
            var Cc = Components.classes;
 
            var converter = Cc['@mozilla.org/intl/scriptableunicodeconverter']
                .createInstance(Ci.nsIScriptableUnicodeConverter);
            converter.charset = 'UTF-8';
            var result = {};
            var data = converter.convertToByteArray(str, result);
 
            var ch = Cc["@mozilla.org/security/hash;1"]
                .createInstance(Ci.nsICryptoHash);
            ch.initWithString('md5');
            ch.update(data, data.length);
                
            var hash = ch.finish(false);
 
            return [
                ('0' + (hash.charCodeAt(i).toString(16)) ).slice(-2)
                for (i in hash)
            ].join("");
        },
 
        /**
* Given an XML doc, render as a template.
*
* Since both JST and E4X use { and } functionally, [[ and ]] are
* used as a convention for JST's syntax.
*/
        renderE4XTemplate: function(xml, ns) {
            var tmpl = xml.toXMLString().replace(/\[\[/g,'{').replace(/\]\]/g, '}');
            return CmdUtils.renderTemplate(tmpl, ns);
        },
 
        /**
* Templates for producing previews and other content.
*/
        templates: {
 
            // Preview displayed when no user login found.
            preview_user_error:
                <p style="color: #d44">
                    No active user found - log in at
                    <img src="http://delicious.com/favicon.ico" />
                    <b><a style="color: #3774D0" href="http://delicious.com">delicious.com</a></b>
                    to use this command.
                </p>,
 
            // Preview displayed when title missing.
            preview_title_error:
                <p style="color: #d44">
                    A title is required for bookmarks on
                    <img src="http://delicious.com/favicon.ico" />
                    <b><a style="color: #3774D0" href="http://delicious.com">delicious.com</a></b>
                </p>,
 
            // Preview showing full bookmark data.
            preview_full:
                <div class="preview">
                    <style type="text/css"><![CDATA[
                        .preview a { color: #3774D0 }
                        .del-bookmark { font: 12px arial; color: #ddd; background: #eee; line-height: 1.25em; padding: 1em }
                        .del-bookmark a.title { color: #1259C7 }
                        .del-bookmark .full-url { color: #396C9B; font-size: 12px; display: block; padding: 0.25em 0 }
                        .del-bookmark .notes { color: #4D4D4D }
                        .del-bookmark .counter { color: #999; text-align: right; padding-top: 0.5em; font-size: 85% }
                        .del-bookmark .counter .over { color: #c44; font-weight: bold }
                        .del-bookmark .tags { color: #787878; padding-top: 0.25em; text-align: right }
                    ]]></style>
                    <p>Share a bookmark at <img src="http://delicious.com/favicon.ico" />
                        <b><a href="http://delicious.com/$[[user_name]]">delicious.com/$[[user_name]]</a></b>
                        <div class="del-bookmark">
                            <a class="title" href="$[[bm.url]]">$[[bm.description]]</a>
                            <a class="full-url" href="$[[bm.url]]">$[[bm.url]]</a>
                            [[if bm.extended]]
                                <div class="notes">$[[bm.extended]]</div>
                                <div class="counter"><span class="$[[ (chars_over) ? 'over' : 'under' ]]">
                                    $[[chars_left]] characters $[[ (chars_over) ? 'over' : 'remaining' ]]
                                </span></div>
                            [[/if]]
                            [[if bm.tags]]<div class="tags"><span>tags:</span> $[[bm.tags]]</div>[[/if]]
                        </div>
                    </p>
                </div>
 
        },
 
        /**
* Original flat 4-color delicious logo.
*/
        DEL_ICON_URL_OLD: <data><![CDATA[
            data:image/gif;base64,R0lGODlhEAAQAKIAAAAAAP///zJ00NPS0v//
            /wAAAAAAAAAAACH5BAEAAAQALAAAAAAQABAAAAMsGLoq/o4xCOWizzYsdO
            CdBnqjyAFoig5sy6qq68Kp3NKrPeCAvuM+XhCoSwAAOw==
        ]]></data>.toString().replace(/\s/g,''),
 
        /**
* Sexier gradient delicious logo.
*/
        DEL_ICON_URL: <data><![CDATA[
            data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAY
            AAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3No
            b3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi
            4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6
            OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8T
            G4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH
            /w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjA
            FAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwAB
            RmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AIS
            ZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJh
            mkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiY
            uP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoO
            naV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7
            iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41ES
            cY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQW
            HTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz
            /HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKay
            CQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgT
            YSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9c
            gI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJ
            vQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAy
            rxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJ
            NwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ
            7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5D
            PkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9
            aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX
            0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrm
            OeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rk
            ZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFG
            gsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNK
            M1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXl
            lirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpx
            ZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/V
            HDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VG
            jUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1
            pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVV
            pds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWF
            nYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F
            9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbx
            t3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+
            BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M
            +4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGB
            WwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOk
            c5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJ
            ZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5
            svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAq
            qBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlL
            OW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6
            xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZa
            rnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUF
            K4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1
            +1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuW
            TPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO
            /PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7J
            vttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1
            R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTrado
            x7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl
            5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/
            i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nue
            r21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72X
            cn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9
            DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO
            0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT
            +R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAI
            CDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAalJREFUeNqk0rG
            O0zAcx/GvU6dqdWpACmKALUiIserM3tdgZeOp+iB5gltBJ2ikolMpoaR1
            ksaO7RtCW0XNTWSx9Psrn/wcW3jv+Z9HrlYrH0URQojewHvP91zwS8lrd
            pnB69EjXz5/EjJJEhaLBcAFObd6eKzJjy21dqiT5XiyFJXlb2nRv3ddA2
            stUspegzOQHzS7QtM6T+sA55lIeDENaFx7Bay1CCF6Dbz3GOs4tR7Tepr
            WU2tPqT3q5Ahr3QF1XaO1ZjQa9QDn3L/qrvfioXaoquJVVXVAnudst1vC
            MERKSRiGAARBwKGy5Mrx51CiasNRFTRVgW32zFzRAW3bEoYh1lq01jjnL
            lu4/7ZjvWt6pxOM7wjGd7T7rAPSNCVN097PO68v37zn47sPN2cvhOBHce
            qA9Xr97CVZLpckydtB4OfX7iPSGEPTNINA0zRorW/yIAgwxnSA1pqyLAc
            BpdTgTErZB5RSg0BRFOz3+5t8Op1eAWPMsw3yPGc2m93kURRdAYD5fD4I
            xHHMZDK5ycfj8RWI45gkSQaBLMvIsmxwttlsAHgaAK7VFk0V7prNAAAAA
            ElFTkSuQmCC
        ]]></data>.toString().replace(/\s/g,''),
 
        // I hate trailing commas.
        EOF:null
    };
})().init();
 
// Initialize the command, register handlers for startup and page load.
// TODO: Find a way to roll all of this into init()?
share_on_delicious_cmd.init();
function startup_share_on_delicious_cmd() {
    share_on_delicious_cmd.onStartup();
}
function pageLoad_share_on_delicious_cmd(doc) {
    share_on_delicious_cmd.onPageLoad(doc);
}