aboutsummaryrefslogtreecommitdiffstats
path: root/imageextender.js
blob: 357812919fdaae0b864975c336d0a77e81a2540e (plain)
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
/*
 * ==VimperatorPlugin==
 * @name            imageextender.js
 * @description     extend image operation.
 * @description-ja  画像操作特集。
 * @author          janus_wel <janus_wel@fb3.so-net.ne.jp>
 * @version         0.10
 * @minversion      2.0pre
 * @maxversion      2.0pre
 * ==/VimperatorPlugin==
 *
 * LICENSE
 *  New BSD License
 *
 * USAGE
 *  extended-hints mode ':m' to yank image URL and
 *  ';i' to save image are available.
 *  there are default setting.
 *  you can change there by below settings.
 *
 *  command ':downimageall' is also available.
 *  it is to download all images of current page,
 *  but it effects heavy load to the server,
 *  you must use carefully.
 *  we are NOT RESPONSIBLE for result of this command.
 *
 * SETTING
 *  image_extender_yank_key:    key name to yank image URL.
 *                              default is 'm'.
 *  image_extender_save_key:    key name to save image.
 *                              default is 'i'.
 *  image_extender_skip_prompt: if set 'true', skip prompt to locate and name.
 *                              the reflection of this setting is dynamic.
 *                              ':downimageall' command ignore this setting.
 *                              default is 'false'
 *
 * EXAMPLE
 *  in .vimperatorrc
 *
 *      image_extender_yank_key='g'
 *      image_extender_save_key='e'
 *      image_skip_prompt='true'
 *
 *  in this settings, ';g' start extended-hints mode to yank image URL.
 *  ';e' start it to save image. prompt is not displayed at save operation.
 *
 * */

( function () {

// default settings
const yankKey = liberator.globalVariables.image_extender_yank_key || 'm';
const saveKey = liberator.globalVariables.image_extender_save_key || 'i';

// common settings
const query = '//img[@src and not(starts-with(@src, "data:"))]';
const interval = 200; // 5 images per second

// extended-hints mode
// to yank image URL
hints.addMode(
    yankKey,
    'Yank image URL',
    function (element) util.copyToClipboard(element.src, true),
    function () query
);
// to save image
hints.addMode(
    saveKey,
    'Save image',
    function (element) {
        let skipPrompt = stringToBoolean(liberator.globalVariables.image_extender_skip_prompt, false);

        try       { saveImage(element, skipPrompt); }
        catch (e) { liberator.echoerr(e); }
    },
    function () query
);
commands.addUserCommand(
    ['downimageall'],
    'download all images of current page',
    function () {
        // refer: http://d.hatena.ne.jp/amachang/20071108/1194501306
        let images = buffer.evaluateXPath(query);
        let l = images.snapshotLength;
        let i = 0;
        setTimeout ( function a() {
            if (!(i < l)) return;
            try       { saveImage(images.snapshotItem(i), true); }
            catch (e) { liberator.echoerr(e); }
            ++i;
            setTimeout(a, interval);
        }, interval);
    },
    {}
);

// stuff function
function stringToBoolean(str, defaultValue) {
    return !str                          ? (defaultValue ? true : false)
         : str.toLowerCase() === 'false' ? false
         : /^\d+$/.test(str)             ? (parseInt(str) ? true : false)
         :                                 true;
}

function saveImage(imgElement, skipPrompt) {
    let doc = imgElement.ownerDocument;
    let url = imgElement.src;
    let filename = url.split(/\/+/g).pop();

    urlSecurityCheck(url, doc.nodePrincipal);
    // we always want to save that link relative to the current working directory
    options.setPref("browser.download.lastDir", io.getCurrentDirectory().path);
    saveImageURL(url, filename, null, true, skipPrompt, makeURI(url, doc.characterSet));
}
} )();

// vim: sw=4 sts=4 ts=4 et
class="mf">0.1.17) @see http://coderepos.org/share/browser/lang/javascript/vimperator-plugins/trunk/_libly.js == SETTING == Scenario list is loaded by script under RUNTIME_PATH/plugin/scenario, and liberator.globalVariables.userScenario. liberator.globalVariables.userScenario can be set by .vimperatorrc (or _vimperatorrc) using inline JavaScript. >|| // hatena sample javascript <<EOM liberator.globalVariables.userScenario = { DOMContentLoaded: [ { // good by hatena keyword pattern: 'http://d.hatena.ne.jp/keyword/', action: [ {sleep: 1000}, {follow: '//a[contains(@href,"http://ja.wikipedia.org/wiki")]'}, ], }, { // auto paging hatena in 5 seconds pattern: 'hatena.ne.jp', action: {and: [ {sleep: 5000}, {follow: '//a[@rel="prev"]'}, ]} }, { // recent vector site is confusing pattern: 'http://www.vector.co.jp/soft/', action: {or: [ {follow: ['//a[not(starts-with(@href,"http")) and contains(@href,"/soft/dl/")]', liberator.NEW_TAB]}, {follow: '//a[not(starts-with(@href,"http")) and contains(@href,"/download/file/")]'}, ]} }, ], load: [ { // auto hatena star pattern: 'hatena.ne.jp/', action: {follow: '//img[@class="hatena-star-add-button"]'}, }, ], }; EOM ||< Action expressions like >|| action: {and: [ {sleep: 5000}, {follow: '//a[@rel="prev"]'}, ]} ||< is syntax-sugar of >|| action: [{and: [ {sleep: [5000]}, {follow: ['//a[@rel="prev"]']}, ]}] ||< and action expressions are quoted by {begin: ...}. == TODO == - write more examples. - add more functions. - fix bugs. - a lot. ]]></detail> </VimperatorPlugin>; //}}} (function() { const debugMode = true; const VariablesName = 'ScenarioActorVariables'; const VariablesLabelID = 'ScenarioActorVariablesLabelID'; let SCENARIO_DIR = liberator.globalVariables.scenarioDir || 'scenario'; var libly = liberator.plugins.libly; var $U = libly.$U; var logger = $U.getLogger('scenario-actor'); function ScenarioActor () { //{{{ let variables = storage.newMap('scenarioactor', true); function ScenarioContext(event) { //{{{ let triggeredEvent = event; let win = (event.target.contentDocument||event.target).defaultView; let self = { glet: function (name, value) { if((typeof name)!='string') throw [name, value]; variables.set(name, self.eval(value)); return value; }, gvar: function (name) { if((typeof name)!='string') throw [name, value]; return variables.get(name); }, begin: function () { let lastValue; for(let i=0,len=arguments.length;i<len;++i) { lastValue = self.eval(arguments[i]); } return lastValue; }, and: function () { let lastValue; for(let i=0,len=arguments.length;i<len;++i) { if(!(lastValue = self.eval(arguments[i]))) break; } return lastValue; }, or: function () { let lastValue; for(let i=0,len=arguments.length;i<len;++i) { if(lastValue = self.eval(arguments[i])) break; } return lastValue; }, loop: function (cond, exp) { let mainThread = services.get('threadManager').mainThread; let f = function() { if(!self.eval(cond)) return; self.eval(exp); mainThread.processNextEvent(true); f(); }; setTimeout(f, 1); }, get: function (dst, prop) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, prop]; return edst[prop]; }, set: function (dst, src, prop) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, src, prop]; edst[prop] = self.eval(src); return edst[prop]; }, xpath: function (xpath) { if((typeof xpath)!='string'||!win.document) throw [name, value]; return util.evaluateXPath(xpath, win.document).snapshotItem(0); }, value: function (dst, src) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, src]; if(src!=undefined) { // set edst.value = self.eval(src); } return edst.value; }, click: function (dst) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst]; edst.click(); return edst; }, follow: function(dst, where) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, where]; buffer.followLink(edst, where?where:liberator.CURRENT_TAB) return edst; }, remove: function (dst) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst]; edst.parentNode.removeChild(edst); return edst; }, saveLink: function (dst, skipPrompt) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, skipPrompt]; buffer.saveLink(edst, skipPrompt); return edst; }, innerText: function (dst, src) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, src]; if(src==undefined) { // get return edst.innerText; } let esrc = self.eval(src); edst.innerText = esrc; return esrc; }, innerHTML: function (dst, src) { let edst = self.eval({xpath: self.eval(dst)}); if(!edst) throw [dst, src]; if(src==undefined) { // get return edst.innerHTML; } let esrc = self.eval(src); edst.innerHTML = esrc; return esrc; }, url: function() { if(!win.document) throw []; return win.document.location.href; }, prompt: function(message, init) { if(!win) throw [message, init]; return win.prompt(message||'', init||''); }, sleep: function(delay) { return liberator.sleep(delay); }, close: function() { if(!win) throw []; return win.close(); }, jseval: function() { let f = arguments[0]; let args = Array.prototype.slice.call(arguments); args.shift(); if(typeof f!='function') throw [f].concat(args); return f.apply(this, args); }, showVariables: function (names) { if(!win.document) throw [names]; actor.showVariables(win.document, names); }, eval: function(exp) { switch(typeof exp) { default: case 'bolean': case 'number': case 'string': case 'function': return exp; case 'object': for(let sym in exp) { let args = exp[sym]; if(debugMode) logger.log('{'+sym+': '+args+'}'); try { if(args instanceof Array) { return self[sym].apply(this, args); } return self[sym](args); } catch(args if args instanceof Array) { let msg = '{'+sym+': ['+args.join(',')+']}'; liberator.reportError(msg); return false; } } } }, }; return self; } //}}} function createLabel(doc, labelID) { let label; if(!(label = doc.getElementById(labelID))) { label = doc.createElement('pre'); label.id = VariablesLabelID; label.style.position = 'absolute'; label.style.top = '0px'; label.style.left = '0px'; label.style.margin = '0px'; label.style.padding = '0px'; label.style.fontSize = '80%'; label.style.border = '1px solid #ccc'; label.style.backgroundColor = '#fff'; label.style.textAlign = 'left'; label.style.zIndex = '100'; doc.body.appendChild(label); } return label; } function urlmatcher(url) { return function(scenario) { if(scenario.pattern) { let pattern = scenario.pattern; switch(typeof pattern) { case 'string': if(url.indexOf(pattern)==-1) return false; break; case 'object': if(!pattern.test(url)) return false; break; } } if(scenario.ignore) { let ignore = scenario.ignore; switch(typeof ignore) { case 'string': if(url.indexOf(ignore)!=-1) return false; break; case 'object': if(ignore.test(url)) return false; break; } } return true; } } let self = { enabled: true, readScenarioDirectory: function(name, fun) { io.getRuntimeDirectories(SCENARIO_DIR).forEach(function(dir) { $U.readDirectory(dir.path, name, fun); }); }, loadLocalScenario: function(name) { if(!name) name = '\.js$'; if(liberator.globalVariables.userScenario) loadedScenarioList.push(liberator.globalVariables.userScenario); self.readScenarioDirectory(name, function(file) { logger.log('load scenario '+file.path); io.source(file.path); }); loadedScenarioList.forEach(function(list) { for(let event in list) { self.addListener(event, list[event]); } }); }, showVariables: function(doc, names) { if(!doc) doc = window.content.document; if(!names) { names = [name for([name, value] in variables)]; } let label = createLabel(doc, VariablesLabelID); label.innerHTML = [arg+': '+variables.get(arg) for each(arg in names)].join("\n"); }, clear: function () { variables.clear(); }, addListener: function (eventType, scenarioList) { if(!scenarioList || scenarioList.length==0) return scenarioList; getBrowser().addEventListener(eventType, function (event) { if (!self.enabled) return; let context = ScenarioContext(event); let url = context.url(); if(!url) return false; let matchfun = urlmatcher(url); scenarioList.forEach(function(scenario) { if(matchfun(scenario)) { context.eval({begin: scenario.action}); } }); }, true); }, }; return self; }; //}}} let actor = plugins.scenarioActor = ScenarioActor(); let loadedScenarioList = plugins.scenarioActor.loadedScenarioList = []; actor.enabled = !/^(?:false|0)$/i.test(liberator.globalVariables.scenario_actor_enabled); actor.loadLocalScenario(); commands.addUserCommand(['scenarioclear'], 'clear scenario-actor variables', actor.clear, { argCount: '0', }); commands.addUserCommand(['scenariovars'], 'show scenario-actor variables', function(args) { actor.showVariables(window.content.document); }, { argCount: '0', }); commands.addUserCommand(['scenarioload'], 'load local scenario', function(args) { if(args.length) { args.forEach(actor.loadLocalScenario); } else { actor.loadLocalScenario(); } }, { argCount: '*', completer: function (context, args) { let completions = []; actor.readScenarioDirectory(name, function(file) { completions.push([file.leafName, '']); }); context.completions = completions; } }); commands.addUserCommand(['scenario'], 'turn on/off scenario-actor', function(args) { if (args.length) { switch (args[0].toLowerCase()) { case 'on': actor.enabled = true; break; case 'off': actor.enabled = false; break; default: liberator.echoerr('Invalid argument: ' + args[0]); return; } } else { actor.enabled = !actor.enabled; } liberator.echo('scenario-actor was ' + (actor.enabled ? 'enabled' : 'disabled')); }, { argCount: '*', completer: function (context, args) { context.completions = [['on', 'enable scenario-actor'], ['off', 'disable scenario-actor']]; } }); })(); // vim: set fdm=marker sw=4 ts=4 et: