Skip to content

Instantly share code, notes, and snippets.

@LouCypher
Last active May 26, 2016 00:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save LouCypher/3218906 to your computer and use it in GitHub Desktop.
Save LouCypher/3218906 to your computer and use it in GitHub Desktop.
Use external editor to edit style on Stylish extension
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above.
*
*
* The Original Code is the External Editor extension.
* The Initial Developer of the above Original Code is
* Philip Nilsson.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kimitake
* Supported Japanese charaset and added ja-JP locale
*
* The Original Code is the MozEx extension.
* Copyright (C) 2003 Tomas Styblo <tripie@cpan.org>
*
* Contributor(s):
* - Alice0775, External Edittor for Custum Buttons
* http://space.geocities.yahoo.co.jp/gl/alice0775
* (2007/02/21)
* - LouCypher, external editor for Stylish extension
*
*
* ***** END LICENSE BLOCK ***** */
(function(aTarget) {
var Cc = Components.classes;
var Ci = Components.interfaces;
var _tmpdir = null, _dir_separator, _os;
var _ext, _encode, _target = [];
var _file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
if (window.navigator.platform.toLowerCase().indexOf("win") != -1) {
// Windows OS
_dir_separator = "\\";
_os = "win";
} else {
// UNIX/Linux OS
_dir_separator = "/";
_os = "unix";
}
_ext = "css";
_encode = "UTF-8";
_target = [];
window.addEventListener("unload", edituninit, false);
window.addEventListener("unload", function() {
document.removeEventListener("focus", checkfocus_window, true);
}, false);
function getEditor() {
var pref = Services.prefs.getBranch("extensions.stylish.");
var editor = null;
try {
editor = pref.getCharPref("editor.external");
} catch(ex) {}
if (!editor) {
var prompts = Services.prompt;
var ask = prompts.confirmEx(null, "Stylish Editor",
"Select external editor to use with Stylish",
prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_IS_STRING
+ prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_CANCEL
+ prompts.BUTTON_POS_2 * prompts.BUTTON_TITLE_IS_STRING,
"Continue", "", "", null, {value: false});
if (ask != 0) return;
var nsIFilePicker = Ci.nsIFilePicker;
var filePicker = Cc["@mozilla.org/filepicker;1"].
createInstance(nsIFilePicker);
filePicker.init(window, "Select editor", nsIFilePicker.modeOpen);
filePicker.appendFilters(nsIFilePicker.filterApplication);
filePicker.appendFilters(nsIFilePicker.filterAll);
if (filePicker.show() == nsIFilePicker.returnOK) {
if (filePicker.file.exists() && filePicker.file.isExecutable()) {
pref.setCharPref("editor.external", filePicker.file.path);
editor = filePicker.file.path;
}
}
}
return editor;
}
function edituninit() {
if (_tmpdir == null) return;
var windowType = "navigator:browser";
var enumerator = Services.wm.getEnumerator(windowType);
if (enumerator.hasMoreElements()) {
return;
}
_file.initWithPath(_tmpdir);
var entries = _file.directoryEntries;
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
if (/^stylish\./i.test(entry.leafName)) {
try {
entry.remove(false);
} catch(e) {
}
}
}
try {
if (_file.exists() == true ) {
_file.remove(false);
}
} catch(e) {
}
_tmpdir = null;
}
function checkfocus_window() {
var target, filename, timestamp, encode,
inst, sstream, utf, textBoxText;
if (_target.length <= 0) return;
istr = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// FileInputStream's read is [noscript].
sstream = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
utf = Cc["@mozilla.org/intl/utf8converterservice;1"].
createInstance(Ci.nsIUTF8ConverterService);
for (var i = 0; i < _target.length;i++) {
target = _target[i];
if (!target.hasAttribute("filename")) continue;
filename = target.getAttribute("filename");
timestamp = target.getAttribute("timestamp");
_file.initWithPath(filename);
if (!_file.exists() || !_file.isReadable()) continue;
if (_file.lastModifiedTime <= timestamp) continue;
target.setAttribute("timestamp", _file.lastModifiedTime);
istr.init(_file, 1, 0x400, false);
sstream.init(istr);
textBoxText = sstream.read(sstream.available());
textBoxText = textBoxText.replace(/\r\n/g, "\n");
encode = target.getAttribute("encode");
if (textBoxText.length) {
target.value = utf.convertStringToUTF8(textBoxText, encode, true, true);
} else {
target.value = "";
}
if ("SourceEditor" in window)
se.setText(target.value);
if ("sourceEditor" in window)
sourceEditor.setText(target.value);
sstream.close();
istr.close();
try {
_file.remove(false);
} catch(e) {
}
}
}
function editfile(target, filename) {
// Figure out what editor to use.
var editor = getEditor();
if (!editor) return false;
_file.initWithPath(editor);
if (!_file.exists()) {
alert("Error_invalid_Editor_file");
return false;
}
if (!_file.isExecutable()) {
alert("Please pick an executable application.");
return false;
}
target.setAttribute("filename", filename);
target.setAttribute("timestamp", _file.lastModifiedTime);
// Run the editor.
var process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
process.init(_file);
var args = [filename];
process.run(false, args, args.length); // don't block
document.addEventListener("focus", checkfocus_window, true);
return true;
}
function edittarget(target) {
//var textBoxText = target.value;
var textBoxText = "SourceEditor" in window
? se.getText()
: "sourceEditor" in window
? sourceEditor.getText()
: codeE.value;
// Get filename.
if (target.hasAttribute("filename")) {
var filename = target.getAttribute("filename");
_file.initWithPath(filename);
try {
if(_file.exists()) _file.remove(false);
} catch(e) {
}
} else {
var filename = TmpFilenameTextarea();
}
_file.initWithPath(filename);
_file.create(_file.NORMAL_FILE_TYPE, 0600);
// Write the data to the file.
var ostr = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostr.init(_file, 2, 0x200, false);
if(navigator.platform == "Win32") {
// Convert Unix newlines to standard network newlines
textBoxText = textBoxText.replace(/\r/g, "").replace(/\n/g, "\r\n");
}
var conv = Cc["@mozilla.org/intl/saveascharset;1"].
createInstance(Ci.nsISaveAsCharset);
try {
conv.Init(_encode, 0, 0);
textBoxText = conv.Convert(textBoxText);
} catch(e) {
textBoxText = "";
}
ostr.write(textBoxText, textBoxText.length);
ostr.flush();
ostr.close();
// setup target info
target.setAttribute("encode", _encode);
// Edit the file.
if (editfile(target, _file.path)) {
_target.push(target); // Editting target array
}
}
//Compose temporary filename out of
// - tmpdir setting
// - document url
// - textarea name
// - ext suffix
function TmpFilenameTextarea() {
var TmpFilename;
_tmpdir = gettmpDir();
do {
TmpFilename = _tmpdir + _dir_separator + "stylish." +
Math.floor(Math.random() * 100000) + "." + _ext;
} while (!ExistsFile(TmpFilename))
return TmpFilename;
}
//Function returns true if given filename exists
function ExistsFile(filename) {
try {
_file.initWithPath(filename);
return true;
} catch(e) {
return false;
}
}
/**
* Returns the directory where we put files to edit.
* @returns nsIFile The location where we should write editable files.
*/
function gettmpDir() {
/* Where is the directory that we use. */
var fobj = Services.dirsvc.get("TmpD", Ci.nsIFile);
fobj.append("Temp_stylish");
if (!fobj.exists()) {
fobj.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0700", 8));
}
if (!fobj.isDirectory()) {
// the string will be replaced locale properties in the future
alert("Having a problem finding or creating directory: " + fobj.path);
}
return fobj.path;
}
edittarget(aTarget)
})(document.getElementById("internal-code"))
@silverwind
Copy link

It's working nicely, though I wonder if it would be possible to automate hitting the "preview" button when the file is saved on the external editor. Would the Addon API allow watching a file on the disk for changes?

@FireyFly
Copy link

FireyFly commented Feb 1, 2015

The Scriptish extension seems to watch files for changes, to some degree at least, FWIW. I'm not sure how they do it, but it could be worth looking into.

@Arthur-BDR
Copy link

Hi. This page directly comes up on web search (i.e. no other info). I added it to 'Firefox -> Add-ons -> (Greasemonkey) User Scripts' as 'External Editor for Stylish' and, as I could glean from it (I'm not a Javascript expert), created a string entry named extensions.stylish.editor.external in about:config and set it to "C:\Program Files (x86)\Vim\vim74\gvim.exe" (also tried without the quotes), but when I go to 'Firefox -> Add-ons -> (Stylish) User Styles' and click on Edit for one of the scripts, it still comes up in the good old Stylish "editor" in a tab. Does the setting extensions.stylish.editor need to be set to something other than 0? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment