mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge m-c to fx-team
This commit is contained in:
commit
59de8b0c3a
@ -89,7 +89,7 @@
|
||||
<command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
|
||||
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
|
||||
<command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
|
||||
<command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
|
||||
<command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
|
||||
<command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
|
||||
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
|
||||
<command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
|
||||
|
@ -41,7 +41,7 @@ gcli.addCommand({
|
||||
manual: gcli.lookup("screenshotFullPageManual")
|
||||
},
|
||||
{
|
||||
name: "node",
|
||||
name: "selector",
|
||||
type: "node",
|
||||
defaultValue: null,
|
||||
description: gcli.lookup("inspectNodeDesc"),
|
||||
@ -59,7 +59,7 @@ gcli.addCommand({
|
||||
return promise;
|
||||
}
|
||||
else {
|
||||
return this.grabScreen(document, args.filename, args.fullpage, args.node);
|
||||
return this.grabScreen(document, args.filename, args.fullpage, args.selector);
|
||||
}
|
||||
},
|
||||
grabScreen:
|
||||
|
@ -1707,7 +1707,7 @@ var types = require('gcli/types');
|
||||
var Type = require('gcli/types').Type;
|
||||
var Status = require('gcli/types').Status;
|
||||
var Conversion = require('gcli/types').Conversion;
|
||||
var Speller = require('gcli/types/spell').Speller;
|
||||
var spell = require('gcli/types/spell');
|
||||
|
||||
|
||||
/**
|
||||
@ -1888,13 +1888,14 @@ SelectionType.prototype._findPredictions = function(arg) {
|
||||
}
|
||||
|
||||
// Try fuzzy matching if we don't get a prefix match
|
||||
if (false && predictions.length === 0) {
|
||||
var speller = new Speller();
|
||||
var names = lookup.map(function(opt) {
|
||||
return opt.name;
|
||||
if (predictions.length === 0) {
|
||||
var names = [];
|
||||
lookup.forEach(function(opt) {
|
||||
if (!opt.value.hidden) {
|
||||
names.push(opt.name);
|
||||
}
|
||||
});
|
||||
speller.train(names);
|
||||
var corrected = speller.correct(match);
|
||||
var corrected = spell.correct(match, names);
|
||||
if (corrected) {
|
||||
lookup.forEach(function(opt) {
|
||||
if (opt.name === corrected) {
|
||||
@ -2005,154 +2006,117 @@ exports.SelectionType = SelectionType;
|
||||
|
||||
});
|
||||
/*
|
||||
* Copyright (c) 2009 Panagiotis Astithas
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
* Copyright 2012, Mozilla Foundation and contributors
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
define('gcli/types/spell', ['require', 'exports', 'module' ], function(require, exports, module) {
|
||||
|
||||
/*
|
||||
* A spell-checker based on Damerau-Levenshtein distance.
|
||||
*/
|
||||
|
||||
var INSERTION_COST = 1;
|
||||
var DELETION_COST = 1;
|
||||
var SWAP_COST = 1;
|
||||
var SUBSTITUTION_COST = 2;
|
||||
var MAX_EDIT_DISTANCE = 4;
|
||||
|
||||
/**
|
||||
* A spell-checker based on the statistical algorithm described by Peter Norvig
|
||||
* in http://norvig.com/spell-correct.html, and converted to JavaScript by Past
|
||||
* http://past.github.com/speller/
|
||||
*
|
||||
* Usage requires a two-step process:
|
||||
* 1) call speller.train() one or more times with a large text to train the
|
||||
* language model
|
||||
* 2) call speller.correct(word) to retrieve the correction for the specified
|
||||
* word
|
||||
* Compute Damerau-Levenshtein Distance
|
||||
* @see http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
|
||||
*/
|
||||
function Speller() {
|
||||
// A map of words to the count of times they were encountered during training.
|
||||
this._nWords = {};
|
||||
}
|
||||
function damerauLevenshteinDistance(wordi, wordj) {
|
||||
var N = wordi.length;
|
||||
var M = wordj.length;
|
||||
|
||||
Speller.letters = "abcdefghijklmnopqrstuvwxyz".split("");
|
||||
// We only need to store three rows of our dynamic programming matrix.
|
||||
// (Without swap, it would have been two.)
|
||||
var row0 = new Array(N+1);
|
||||
var row1 = new Array(N+1);
|
||||
var row2 = new Array(N+1);
|
||||
var tmp;
|
||||
|
||||
/**
|
||||
* A function that trains the language model with the words in the supplied
|
||||
* text. Multiple invocation of this function can extend the training of the
|
||||
* model.
|
||||
*/
|
||||
Speller.prototype.train = function(words) {
|
||||
words.forEach(function(word) {
|
||||
word = word.toLowerCase();
|
||||
this._nWords[word] = this._nWords.hasOwnProperty(word) ?
|
||||
this._nWords[word] + 1 :
|
||||
1;
|
||||
}, this);
|
||||
var i, j;
|
||||
|
||||
// The distance between the empty string and a string of size i is the cost
|
||||
// of i insertions.
|
||||
for (i = 0; i <= N; i++) {
|
||||
row1[i] = i * INSERTION_COST;
|
||||
}
|
||||
|
||||
// Row-by-row, we're computing the edit distance between substrings wordi[0..i]
|
||||
// and wordj[0..j].
|
||||
for (j = 1; j <= M; j++)
|
||||
{
|
||||
// Edit distance between wordi[0..0] and wordj[0..j] is the cost of j
|
||||
// insertions.
|
||||
row0[0] = j * INSERTION_COST;
|
||||
|
||||
for (i = 1; i <= N; i++)
|
||||
{
|
||||
// Handle deletion, insertion and substitution: we can reach each cell
|
||||
// from three other cells corresponding to those three operations. We
|
||||
// want the minimum cost.
|
||||
row0[i] = Math.min(
|
||||
row0[i-1] + DELETION_COST,
|
||||
row1[i] + INSERTION_COST,
|
||||
row1[i-1] + (wordi[i-1] === wordj[j-1] ? 0 : SUBSTITUTION_COST));
|
||||
// We handle swap too, eg. distance between help and hlep should be 1. If
|
||||
// we find such a swap, there's a chance to update row0[1] to be lower.
|
||||
if (i > 1 && j > 1 && wordi[i-1] === wordj[j-2] && wordj[j-1] === wordi[i-2]) {
|
||||
row0[i] = Math.min(row0[i], row2[i-2] + SWAP_COST);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = row2;
|
||||
row2 = row1;
|
||||
row1 = row0;
|
||||
row0 = tmp;
|
||||
}
|
||||
|
||||
return row1[N];
|
||||
};
|
||||
|
||||
/**
|
||||
* A function that returns the correction for the specified word.
|
||||
*/
|
||||
Speller.prototype.correct = function(word) {
|
||||
if (this._nWords.hasOwnProperty(word)) {
|
||||
return word;
|
||||
}
|
||||
exports.correct = function(word, names) {
|
||||
var distance = {};
|
||||
var sorted_candidates;
|
||||
|
||||
var candidates = {};
|
||||
var list = this._edits(word);
|
||||
list.forEach(function(edit) {
|
||||
if (this._nWords.hasOwnProperty(edit)) {
|
||||
candidates[this._nWords[edit]] = edit;
|
||||
names.forEach(function(candidate) {
|
||||
distance[candidate] = damerauLevenshteinDistance(word, candidate);
|
||||
});
|
||||
|
||||
sorted_candidates = names.sort(function(worda, wordb) {
|
||||
if (distance[worda] !== distance[wordb]) {
|
||||
return distance[worda] - distance[wordb];
|
||||
} else {
|
||||
// if the score is the same, always return the first string
|
||||
// in the lexicographical order
|
||||
return worda < wordb;
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
|
||||
if (this._countKeys(candidates) > 0) {
|
||||
return candidates[this._max(candidates)];
|
||||
if (distance[sorted_candidates[0]] <= MAX_EDIT_DISTANCE) {
|
||||
return sorted_candidates[0];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
list.forEach(function(edit) {
|
||||
this._edits(edit).forEach(function(w) {
|
||||
if (this._nWords.hasOwnProperty(w)) {
|
||||
candidates[this._nWords[w]] = w;
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
return this._countKeys(candidates) > 0 ?
|
||||
candidates[this._max(candidates)] :
|
||||
null;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function that counts the keys in the supplied object.
|
||||
*/
|
||||
Speller.prototype._countKeys = function(object) {
|
||||
// return Object.keys(object).length; ?
|
||||
var count = 0;
|
||||
for (var attr in object) {
|
||||
if (object.hasOwnProperty(attr)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function that returns the word with the most occurrences in the
|
||||
* language model, among the supplied candidates.
|
||||
* @param candidates
|
||||
*/
|
||||
Speller.prototype._max = function(candidates) {
|
||||
var arr = [];
|
||||
for (var candidate in candidates) {
|
||||
if (candidates.hasOwnProperty(candidate)) {
|
||||
arr.push(candidate);
|
||||
}
|
||||
}
|
||||
return Math.max.apply(null, arr);
|
||||
};
|
||||
|
||||
/**
|
||||
* A function that returns the set of possible corrections of the specified
|
||||
* word. The edits can be deletions, insertions, alterations or transpositions.
|
||||
*/
|
||||
Speller.prototype._edits = function(word) {
|
||||
var results = [];
|
||||
|
||||
// Deletion
|
||||
for (var i = 0; i < word.length; i++) {
|
||||
results.push(word.slice(0, i) + word.slice(i + 1));
|
||||
}
|
||||
|
||||
// Transposition
|
||||
for (i = 0; i < word.length - 1; i++) {
|
||||
results.push(word.slice(0, i) + word.slice(i + 1, i + 2)
|
||||
+ word.slice(i, i + 1) + word.slice(i + 2));
|
||||
}
|
||||
|
||||
// Alteration
|
||||
for (i = 0; i < word.length; i++) {
|
||||
Speller.letters.forEach(function(l) {
|
||||
results.push(word.slice(0, i) + l + word.slice(i + 1));
|
||||
}, this);
|
||||
}
|
||||
|
||||
// Insertion
|
||||
for (i = 0; i <= word.length; i++) {
|
||||
Speller.letters.forEach(function(l) {
|
||||
results.push(word.slice(0, i) + l + word.slice(i));
|
||||
}, this);
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.Speller = Speller;
|
||||
|
||||
|
||||
});
|
||||
/*
|
||||
@ -4068,7 +4032,9 @@ exports._empty = [];
|
||||
*/
|
||||
exports.setDocument = function(document) {
|
||||
doc = document;
|
||||
exports._empty = doc.querySelectorAll('x>:root');
|
||||
if (doc != null) {
|
||||
exports._empty = doc.querySelectorAll('x>:root');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -5127,7 +5093,7 @@ var view = require('gcli/ui/view');
|
||||
var l10n = require('gcli/l10n');
|
||||
|
||||
var canon = require('gcli/canon');
|
||||
var Promise = require('gcli/promise').Promise;
|
||||
var Q = require('gcli/promise');
|
||||
|
||||
var Status = require('gcli/types').Status;
|
||||
var Conversion = require('gcli/types').Conversion;
|
||||
@ -5190,8 +5156,6 @@ function Assignment(param, paramIndex) {
|
||||
this.paramIndex = paramIndex;
|
||||
|
||||
this.onAssignmentChange = util.createEvent('Assignment.onAssignmentChange');
|
||||
|
||||
this.setBlank();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5275,39 +5239,6 @@ Assignment.prototype.isInName = function() {
|
||||
this.conversion.arg.prefix.slice(-1) !== ' ';
|
||||
};
|
||||
|
||||
/**
|
||||
* Report on the status of the last parse() conversion.
|
||||
* We force mutations to happen through this method rather than have
|
||||
* setValue and setArgument functions to help maintain integrity when we
|
||||
* have ArrayArguments and don't want to get confused. This way assignments
|
||||
* are just containers for a conversion rather than things that store
|
||||
* a connection between an arg/value.
|
||||
* @see types.Conversion
|
||||
*/
|
||||
Assignment.prototype.setConversion = function(conversion) {
|
||||
var oldConversion = this.conversion;
|
||||
|
||||
this.conversion = conversion;
|
||||
this.conversion.assign(this);
|
||||
|
||||
if (this.conversion.equals(oldConversion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onAssignmentChange({
|
||||
assignment: this,
|
||||
conversion: this.conversion,
|
||||
oldConversion: oldConversion
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup an empty value for the conversion by parsing an empty argument.
|
||||
*/
|
||||
Assignment.prototype.setBlank = function() {
|
||||
this.setConversion(this.param.type.getBlank());
|
||||
};
|
||||
|
||||
/**
|
||||
* Make sure that there is some content for this argument by using an
|
||||
* Argument of '' if needed.
|
||||
@ -5454,8 +5385,6 @@ function CommandAssignment() {
|
||||
this.param = new canon.Parameter(commandParamMetadata);
|
||||
this.paramIndex = -1;
|
||||
this.onAssignmentChange = util.createEvent('CommandAssignment.onAssignmentChange');
|
||||
|
||||
this.setBlank();
|
||||
}
|
||||
|
||||
CommandAssignment.prototype = Object.create(Assignment.prototype);
|
||||
@ -5540,6 +5469,7 @@ function Requisition(environment, doc) {
|
||||
// The command that we are about to execute.
|
||||
// @see setCommandConversion()
|
||||
this.commandAssignment = new CommandAssignment();
|
||||
this._setAssignment(this.commandAssignment, null, true);
|
||||
|
||||
// The object that stores of Assignment objects that we are filling out.
|
||||
// The Assignment objects are stored under their param.name for named
|
||||
@ -5626,6 +5556,7 @@ Requisition.prototype._commandAssignmentChanged = function(ev) {
|
||||
for (var i = 0; i < command.params.length; i++) {
|
||||
var param = command.params[i];
|
||||
var assignment = new Assignment(param, i);
|
||||
this._setAssignment(assignment, null, true);
|
||||
assignment.onAssignmentChange.add(this._assignmentChanged, this);
|
||||
this._assignments[param.name] = assignment;
|
||||
}
|
||||
@ -5748,40 +5679,81 @@ Requisition.prototype.getAssignments = function(includeCommand) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Alter the given assignment using the given arg. This function is better than
|
||||
* calling assignment.setConversion(assignment.param.type.parse(arg)) because
|
||||
* it adjusts the args in this requisition to keep things up to date
|
||||
* Alter the given assignment using the given arg.
|
||||
* @param assignment The assignment to alter
|
||||
* @param arg The new value for the assignment. An instance of Argument, or an
|
||||
* instance of Conversion, or null to set the blank value.
|
||||
*/
|
||||
Requisition.prototype.setAssignment = function(assignment, arg) {
|
||||
var originalArgs = assignment.arg.getArgs();
|
||||
var conversion = assignment.param.type.parse(arg);
|
||||
assignment.setConversion(conversion);
|
||||
this._setAssignment(assignment, arg, false);
|
||||
};
|
||||
|
||||
var replacementArgs = arg.getArgs();
|
||||
var maxLen = Math.max(originalArgs.length, replacementArgs.length);
|
||||
for (var i = 0; i < maxLen; i++) {
|
||||
// If there are no more original args, or if the original arg was blank
|
||||
// (i.e. not typed by the user), we'll just need to add at the end
|
||||
if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
|
||||
this._args.push(replacementArgs[i]);
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Internal function to alter the given assignment using the given arg.
|
||||
* @param assignment The assignment to alter
|
||||
* @param arg The new value for the assignment. An instance of Argument, or an
|
||||
* instance of Conversion, or null to set the blank value.
|
||||
* @param skipArgUpdate (default=false) Adjusts the args in this requisition to
|
||||
* keep things up to date. Args should only be skipped when setAssignment is
|
||||
* being called as part of the update process.
|
||||
*/
|
||||
Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate) {
|
||||
if (!skipArgUpdate) {
|
||||
var originalArgs = assignment.arg.getArgs();
|
||||
|
||||
var index = this._args.indexOf(originalArgs[i]);
|
||||
if (index === -1) {
|
||||
console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
|
||||
throw new Error('Couldn\'t find ' + originalArgs[i]);
|
||||
}
|
||||
// Update the args array
|
||||
var replacementArgs = arg.getArgs();
|
||||
var maxLen = Math.max(originalArgs.length, replacementArgs.length);
|
||||
for (var i = 0; i < maxLen; i++) {
|
||||
// If there are no more original args, or if the original arg was blank
|
||||
// (i.e. not typed by the user), we'll just need to add at the end
|
||||
if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
|
||||
this._args.push(replacementArgs[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there are no more replacement args, we just remove the original args
|
||||
// Otherwise swap original args and replacements
|
||||
if (i >= replacementArgs.length) {
|
||||
this._args.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
this._args[index] = replacementArgs[i];
|
||||
var index = this._args.indexOf(originalArgs[i]);
|
||||
if (index === -1) {
|
||||
console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
|
||||
throw new Error('Couldn\'t find ' + originalArgs[i]);
|
||||
}
|
||||
|
||||
// If there are no more replacement args, we just remove the original args
|
||||
// Otherwise swap original args and replacements
|
||||
if (i >= replacementArgs.length) {
|
||||
this._args.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
this._args[index] = replacementArgs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var conversion;
|
||||
if (arg == null) {
|
||||
conversion = assignment.param.type.getBlank();
|
||||
}
|
||||
else if (typeof arg.getStatus === 'function') {
|
||||
conversion = arg;
|
||||
}
|
||||
else {
|
||||
conversion = assignment.param.type.parse(arg);
|
||||
}
|
||||
|
||||
var oldConversion = assignment.conversion;
|
||||
|
||||
assignment.conversion = conversion;
|
||||
assignment.conversion.assign(assignment);
|
||||
|
||||
if (assignment.conversion.equals(oldConversion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assignment.onAssignmentChange({
|
||||
assignment: assignment,
|
||||
conversion: assignment.conversion,
|
||||
oldConversion: oldConversion
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -5789,7 +5761,7 @@ Requisition.prototype.setAssignment = function(assignment, arg) {
|
||||
*/
|
||||
Requisition.prototype.setBlankArguments = function() {
|
||||
this.getAssignments().forEach(function(assignment) {
|
||||
assignment.setBlank();
|
||||
this._setAssignment(assignment, null, true);
|
||||
}, this);
|
||||
};
|
||||
|
||||
@ -6148,8 +6120,8 @@ Requisition.prototype.getAssignmentAt = function(cursor) {
|
||||
* @param input (optional) The command to execute. See above.
|
||||
*/
|
||||
Requisition.prototype.exec = function(input) {
|
||||
var command;
|
||||
var args;
|
||||
var command = null;
|
||||
var args = null;
|
||||
var hidden = false;
|
||||
if (input && input.hidden) {
|
||||
hidden = true;
|
||||
@ -6200,33 +6172,58 @@ Requisition.prototype.exec = function(input) {
|
||||
|
||||
this.commandOutputManager.onOutput({ output: output });
|
||||
|
||||
var onDone = function(data) {
|
||||
output.complete(data);
|
||||
};
|
||||
|
||||
var onError = function(error) {
|
||||
console.error(error);
|
||||
output.error = true;
|
||||
output.complete(error);
|
||||
};
|
||||
|
||||
try {
|
||||
var context = exports.createExecutionContext(this);
|
||||
var reply = command.exec(args, context);
|
||||
|
||||
if (reply != null && typeof reply.then === 'function') {
|
||||
reply.then(
|
||||
function(data) { output.complete(data); },
|
||||
function(error) { output.error = true; output.complete(error); });
|
||||
|
||||
output.promise = reply;
|
||||
// Add progress to our promise and add a handler for it here
|
||||
// See bug 659300
|
||||
}
|
||||
else {
|
||||
output.complete(reply);
|
||||
}
|
||||
this._then(reply, onDone, onError);
|
||||
}
|
||||
catch (ex) {
|
||||
console.error(ex);
|
||||
output.error = true;
|
||||
output.complete(ex);
|
||||
onError(ex);
|
||||
}
|
||||
|
||||
this.update('');
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Different types of promise have different ways of doing 'then'. This is a
|
||||
* catch-all so we can ignore the differences. It also handles concrete values
|
||||
* and calls onDone directly if thing is not a promise.
|
||||
* @param thing The value to test for 'promiseness'
|
||||
* @param onDone The action to take if thing is resolved
|
||||
* @param onError The action to take if thing is rejected
|
||||
*/
|
||||
Requisition.prototype._then = function(thing, onDone, onError) {
|
||||
var then = null;
|
||||
if (thing != null && typeof thing.then === 'function') {
|
||||
// Old GCLI style / simple promises with a then function
|
||||
then = thing.then;
|
||||
}
|
||||
else if (thing != null && thing.promise != null &&
|
||||
typeof thing.promise.then === 'function') {
|
||||
// Q / Mozilla add-ons style
|
||||
then = thing.promise.then;
|
||||
}
|
||||
|
||||
if (then != null) {
|
||||
then(onDone, onError);
|
||||
}
|
||||
else {
|
||||
onDone(thing);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the UI when ever the user interacts with a command line input
|
||||
* @param typed The contents of the input field
|
||||
@ -6517,7 +6514,7 @@ Requisition.prototype._split = function(args) {
|
||||
// Special case: if the user enters { console.log('foo'); } then we need to
|
||||
// use the hidden 'eval' command
|
||||
conversion = new Conversion(evalCommand, new ScriptArgument());
|
||||
this.commandAssignment.setConversion(conversion);
|
||||
this._setAssignment(this.commandAssignment, conversion, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6525,8 +6522,8 @@ Requisition.prototype._split = function(args) {
|
||||
|
||||
while (argsUsed <= args.length) {
|
||||
var arg = (argsUsed === 1) ?
|
||||
args[0] :
|
||||
new MergedArgument(args, 0, argsUsed);
|
||||
args[0] :
|
||||
new MergedArgument(args, 0, argsUsed);
|
||||
conversion = this.commandAssignment.param.type.parse(arg);
|
||||
|
||||
// We only want to carry on if this command is a parent command,
|
||||
@ -6544,7 +6541,7 @@ Requisition.prototype._split = function(args) {
|
||||
argsUsed++;
|
||||
}
|
||||
|
||||
this.commandAssignment.setConversion(conversion);
|
||||
this._setAssignment(this.commandAssignment, conversion, true);
|
||||
|
||||
for (var i = 0; i < argsUsed; i++) {
|
||||
args.shift();
|
||||
@ -6590,11 +6587,8 @@ Requisition.prototype._assign = function(args) {
|
||||
if (this.assignmentCount === 1) {
|
||||
var assignment = this.getAssignment(0);
|
||||
if (assignment.param.type instanceof StringType) {
|
||||
var arg = (args.length === 1) ?
|
||||
args[0] :
|
||||
new MergedArgument(args);
|
||||
var conversion = assignment.param.type.parse(arg);
|
||||
assignment.setConversion(conversion);
|
||||
var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
|
||||
this._setAssignment(assignment, arg, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -6639,8 +6633,7 @@ Requisition.prototype._assign = function(args) {
|
||||
arrayArg.addArgument(arg);
|
||||
}
|
||||
else {
|
||||
var conversion = assignment.param.type.parse(arg);
|
||||
assignment.setConversion(conversion);
|
||||
this._setAssignment(assignment, arg, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -6657,7 +6650,7 @@ Requisition.prototype._assign = function(args) {
|
||||
// If not set positionally, and we can't set it non-positionally,
|
||||
// we have to default it to prevent previous values surviving
|
||||
if (!assignment.param.isPositionalAllowed) {
|
||||
assignment.setBlank();
|
||||
this._setAssignment(assignment, null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6674,7 +6667,7 @@ Requisition.prototype._assign = function(args) {
|
||||
}
|
||||
else {
|
||||
if (args.length === 0) {
|
||||
assignment.setBlank();
|
||||
this._setAssignment(assignment, null, true);
|
||||
}
|
||||
else {
|
||||
var arg = args.splice(0, 1)[0];
|
||||
@ -6688,8 +6681,7 @@ Requisition.prototype._assign = function(args) {
|
||||
this._unassigned.push(new UnassignedAssignment(this, arg));
|
||||
}
|
||||
else {
|
||||
var conversion = assignment.param.type.parse(arg);
|
||||
assignment.setConversion(conversion);
|
||||
this._setAssignment(assignment, arg, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6698,8 +6690,7 @@ Requisition.prototype._assign = function(args) {
|
||||
// Now we need to assign the array argument (if any)
|
||||
Object.keys(arrayArgs).forEach(function(name) {
|
||||
var assignment = this.getAssignment(name);
|
||||
var conversion = assignment.param.type.parse(arrayArgs[name]);
|
||||
assignment.setConversion(conversion);
|
||||
this._setAssignment(assignment, arrayArgs[name], true);
|
||||
}, this);
|
||||
|
||||
// What's left is can't be assigned, but we need to extract
|
||||
@ -6729,17 +6720,31 @@ function Output(options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is data to display
|
||||
* @param data
|
||||
* Called when there is data to display, but the command is still executing
|
||||
* @param data The new data. If the data structure has been altered but the
|
||||
* root object is still the same, The same root object should be passed in the
|
||||
* data parameter.
|
||||
* @param ev Optional additional event data, for example to explain how the
|
||||
* data structure has changed
|
||||
*/
|
||||
Output.prototype.complete = function(data) {
|
||||
Output.prototype.changed = function(data, ev) {
|
||||
this.data = data;
|
||||
|
||||
ev = ev || {};
|
||||
ev.output = this;
|
||||
this.onChange(ev);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when there is data to display, and the command has finished executing
|
||||
* See changed() for details on parameters.
|
||||
*/
|
||||
Output.prototype.complete = function(data, ev) {
|
||||
this.end = new Date();
|
||||
this.duration = this.end.getTime() - this.start.getTime();
|
||||
this.completed = true;
|
||||
|
||||
this.onChange({ output: this });
|
||||
this.changed(data, ev);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -6830,8 +6835,15 @@ exports.createExecutionContext = function(requisition) {
|
||||
document: requisition.document,
|
||||
environment: requisition.environment,
|
||||
createView: view.createView,
|
||||
defer: function() {
|
||||
return Q.defer();
|
||||
},
|
||||
/**
|
||||
* @deprecated Use defer() instead, which does the same thing, but is not
|
||||
* confusingly named
|
||||
*/
|
||||
createPromise: function() {
|
||||
return new Promise();
|
||||
return Q.defer();
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -6856,8 +6868,13 @@ exports.createExecutionContext = function(requisition) {
|
||||
|
||||
define('gcli/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
|
||||
|
||||
Components.utils.import("resource:///modules/devtools/Promise.jsm");
|
||||
exports.Promise = Promise;
|
||||
var imported = {};
|
||||
Components.utils.import("resource://gre/modules/commonjs/promise/core.js",
|
||||
imported);
|
||||
|
||||
exports.defer = imported.Promise.defer;
|
||||
exports.resolve = imported.Promise.resolve;
|
||||
exports.reject = imported.Promise.reject;
|
||||
|
||||
});
|
||||
define("text!gcli/ui/intro.html", [], "\n" +
|
||||
@ -7237,7 +7254,7 @@ FocusManager.prototype._checkShow = function() {
|
||||
|
||||
if (fire) {
|
||||
if (this._debug) {
|
||||
console.debug('FocusManager.onVisibilityChange', ev);
|
||||
console.log('FocusManager.onVisibilityChange', ev);
|
||||
}
|
||||
this.onVisibilityChange(ev);
|
||||
}
|
||||
@ -7735,6 +7752,9 @@ Field.prototype.destroy = function() {
|
||||
delete this.messageElement;
|
||||
};
|
||||
|
||||
// Note: We could/should probably change Fields from working with Conversions
|
||||
// to working with Arguments (Tokens), which makes for less calls to parse()
|
||||
|
||||
/**
|
||||
* Update this field display with the value from this conversion.
|
||||
* Subclasses should provide an implementation of this function.
|
||||
@ -9393,7 +9413,9 @@ Inputter.prototype.textChanged = function() {
|
||||
input.typed = newStr;
|
||||
this._processCaretChange(input);
|
||||
|
||||
this.element.value = newStr;
|
||||
if (this.element.value !== newStr) {
|
||||
this.element.value = newStr;
|
||||
}
|
||||
this.onInputChange({ inputState: input });
|
||||
};
|
||||
|
||||
@ -9469,8 +9491,12 @@ Inputter.prototype._processCaretChange = function(input) {
|
||||
cursor: { start: start, end: end }
|
||||
};
|
||||
|
||||
this.element.selectionStart = start;
|
||||
this.element.selectionEnd = end;
|
||||
if (this.element.selectionStart !== start) {
|
||||
this.element.selectionStart = start;
|
||||
}
|
||||
if (this.element.selectionEnd !== end) {
|
||||
this.element.selectionEnd = end;
|
||||
}
|
||||
|
||||
this._checkAssignment(start);
|
||||
|
||||
@ -9605,7 +9631,7 @@ Inputter.prototype.onKeyUp = function(ev) {
|
||||
// If the user is on a valid value, then we increment the value, but if
|
||||
// they've typed something that's not right we page through predictions
|
||||
if (this.assignment.getStatus() === Status.VALID) {
|
||||
this.requisition.increment(assignment);
|
||||
this.requisition.increment(this.assignment);
|
||||
// See notes on focusManager.onInputChange in onKeyDown
|
||||
if (this.focusManager) {
|
||||
this.focusManager.onInputChange();
|
||||
@ -9629,7 +9655,7 @@ Inputter.prototype.onKeyUp = function(ev) {
|
||||
else {
|
||||
// See notes above for the UP key
|
||||
if (this.assignment.getStatus() === Status.VALID) {
|
||||
this.requisition.decrement(assignment);
|
||||
this.requisition.decrement(this.assignment);
|
||||
// See notes on focusManager.onInputChange in onKeyDown
|
||||
if (this.focusManager) {
|
||||
this.focusManager.onInputChange();
|
||||
@ -10314,7 +10340,7 @@ Tooltip.prototype.selectChoice = function(ev) {
|
||||
* Called by the onFieldChange event on the current Field
|
||||
*/
|
||||
Tooltip.prototype.fieldChanged = function(ev) {
|
||||
this.assignment.setConversion(ev.conversion);
|
||||
this.requisition.setAssignment(this.assignment, ev.conversion.arg);
|
||||
|
||||
var isError = ev.conversion.message != null && ev.conversion.message !== '';
|
||||
this.focusManager.setError(isError);
|
||||
|
@ -163,8 +163,8 @@ function testPrefStatus() {
|
||||
helpers.setInput('pref list');
|
||||
helpers.check({
|
||||
input: 'pref list',
|
||||
hints: '',
|
||||
markup: 'EEEEVEEEE',
|
||||
hints: ' -> pref set',
|
||||
markup: 'IIIIVIIII',
|
||||
status: 'ERROR'
|
||||
});
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ define('gclitest/index', ['require', 'exports', 'module' , 'gclitest/suite', 'gc
|
||||
* @param options Lookup of options that customize test running. Includes:
|
||||
* - window (default=undefined) A reference to the DOM window. If left
|
||||
* undefined then a reduced set of tests will run.
|
||||
* - isNode (default=false) Are we running under NodeJS, specifically, are we
|
||||
* using JSDom, which isn't a 100% complete DOM implementation.
|
||||
* - isJsdom (default=false) Are we running under JSDom, specifically, which
|
||||
* isn't a 100% complete DOM implementation.
|
||||
* Some tests are skipped when using NodeJS.
|
||||
* - display (default=undefined) A reference to a Display implementation.
|
||||
* A reduced set of tests will run if left undefined
|
||||
@ -119,12 +119,12 @@ define('gclitest/index', ['require', 'exports', 'module' , 'gclitest/suite', 'gc
|
||||
* |requisition.exec()| which prevents the display from becoming messed up,
|
||||
* however use of hideExec restricts the set of tests that are run
|
||||
*/
|
||||
exports.run = function(options) {
|
||||
exports.runAsync = function(options, callback) {
|
||||
options = options || {};
|
||||
examiner.mergeDefaultOptions(options);
|
||||
|
||||
examiner.reset();
|
||||
examiner.run(options);
|
||||
examiner.runAsync(options, callback);
|
||||
|
||||
// A better set of default than those specified above, come from the set
|
||||
// that are passed to run().
|
||||
@ -155,13 +155,6 @@ define('gclitest/index', ['require', 'exports', 'module' , 'gclitest/suite', 'gc
|
||||
|
||||
// setTimeout keeps stack traces clear of RequireJS frames
|
||||
window.setTimeout(function() {
|
||||
var options = {
|
||||
window: window,
|
||||
display: window.display,
|
||||
hideExec: true
|
||||
};
|
||||
exports.run(options);
|
||||
|
||||
window.createDebugCheck = function() {
|
||||
require([ 'gclitest/helpers' ], function(helpers) {
|
||||
helpers.setup(options);
|
||||
@ -198,7 +191,15 @@ define('gclitest/index', ['require', 'exports', 'module' , 'gclitest/suite', 'gc
|
||||
mockCommands.setup();
|
||||
});
|
||||
};
|
||||
window.testCommands();
|
||||
|
||||
var options = {
|
||||
window: window,
|
||||
display: window.display,
|
||||
hideExec: true
|
||||
};
|
||||
exports.runAsync(options, function() {
|
||||
window.testCommands();
|
||||
});
|
||||
}, 10);
|
||||
|
||||
return {
|
||||
@ -326,25 +327,6 @@ examiner.mergeDefaultOptions = function(options) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the tests defined in the test suite synchronously
|
||||
*/
|
||||
examiner.run = function(options) {
|
||||
Object.keys(examiner.suites).forEach(function(suiteName) {
|
||||
var suite = examiner.suites[suiteName];
|
||||
suite.run(options);
|
||||
}.bind(this));
|
||||
|
||||
if (options.detailedResultLog) {
|
||||
examiner.detailedResultLog();
|
||||
}
|
||||
else {
|
||||
console.log('Completed test suite');
|
||||
}
|
||||
|
||||
return examiner.suites;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all the tests asynchronously
|
||||
*/
|
||||
@ -477,21 +459,6 @@ Suite.prototype.reset = function() {
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all the tests in this suite synchronously
|
||||
*/
|
||||
Suite.prototype.run = function(options) {
|
||||
if (!this._setup(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(this.tests).forEach(function(testName) {
|
||||
this.tests[testName].run(options);
|
||||
}, this);
|
||||
|
||||
this._shutdown(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all the tests in this suite asynchronously
|
||||
*/
|
||||
@ -636,6 +603,9 @@ function Test(suite, name, func) {
|
||||
this.func = func;
|
||||
this.title = name.replace(/^test/, '').replace(/([A-Z])/g, ' $1');
|
||||
|
||||
this.outstanding = [];
|
||||
this.callback = undefined;
|
||||
|
||||
this.failures = [];
|
||||
this.status = stati.notrun;
|
||||
this.checks = 0;
|
||||
@ -645,16 +615,20 @@ function Test(suite, name, func) {
|
||||
* Reset the test to its original state
|
||||
*/
|
||||
Test.prototype.reset = function() {
|
||||
this.outstanding = [];
|
||||
this.callback = undefined;
|
||||
|
||||
this.failures = [];
|
||||
this.status = stati.notrun;
|
||||
this.checks = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run just a single test
|
||||
* Run all the tests in this suite asynchronously
|
||||
*/
|
||||
Test.prototype.run = function(options) {
|
||||
Test.prototype.runAsync = function(options, callback) {
|
||||
assert.currentTest = this;
|
||||
this.callback = callback;
|
||||
this.status = stati.executing;
|
||||
this.failures = [];
|
||||
this.checks = 0;
|
||||
@ -675,18 +649,20 @@ Test.prototype.run = function(options) {
|
||||
}
|
||||
|
||||
assert.currentTest = null;
|
||||
|
||||
this.checkFinish();
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all the tests in this suite asynchronously
|
||||
* Check to see if the currently executing test is completed (i.e. the list of
|
||||
* outstanding tasks has all been completed)
|
||||
*/
|
||||
Test.prototype.runAsync = function(options, callback) {
|
||||
setTimeout(function() {
|
||||
this.run(options);
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
Test.prototype.checkFinish = function() {
|
||||
if (this.outstanding.length == 0) {
|
||||
if (typeof this.callback === 'function') {
|
||||
this.callback();
|
||||
}
|
||||
}.bind(this), delay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -892,21 +868,25 @@ define('gclitest/testCanon', ['require', 'exports', 'module' , 'gclitest/helpers
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert', 'gcli/util'], function(require, exports, module) {
|
||||
define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert'], function(require, exports, module) {
|
||||
|
||||
|
||||
var test = require('test/assert');
|
||||
var util = require('gcli/util');
|
||||
|
||||
// A copy of this code exists in firefox mochitests; when updated here it
|
||||
// should be updated there too. Hence the use of an exports synonym for non
|
||||
// AMD contexts.
|
||||
var helpers = exports;
|
||||
|
||||
helpers._display = undefined;
|
||||
helpers._options = undefined;
|
||||
|
||||
helpers.setup = function(options) {
|
||||
helpers._options = options;
|
||||
helpers._display = options.display;
|
||||
};
|
||||
|
||||
helpers.shutdown = function(options) {
|
||||
helpers._options = undefined;
|
||||
helpers._display = undefined;
|
||||
};
|
||||
|
||||
@ -1025,6 +1005,16 @@ helpers.setInput = function(typed, cursor) {
|
||||
if (cursor) {
|
||||
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
|
||||
}
|
||||
else {
|
||||
// This is a hack because jsdom appears to not handle cursor updates
|
||||
// in the same way as most browsers.
|
||||
if (helpers._options.isJsdom) {
|
||||
helpers._display.inputter.setCursor({
|
||||
start: typed.length,
|
||||
end: typed.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
helpers._display.focusManager.onInputChange();
|
||||
};
|
||||
@ -1589,7 +1579,7 @@ exports.testElement = function(options) {
|
||||
test.ok(assign1.arg.type === 'BlankArgument');
|
||||
test.is(undefined, assign1.value);
|
||||
|
||||
if (!options.isNode) {
|
||||
if (!options.isJsdom) {
|
||||
update({ typed: 'tse :root', cursor: { start: 9, end: 9 } });
|
||||
test.is( 'VVVVVVVVV', statuses);
|
||||
test.is(Status.VALID, status);
|
||||
@ -1625,7 +1615,7 @@ exports.testElement = function(options) {
|
||||
test.is(undefined, assign1.value);
|
||||
}
|
||||
else {
|
||||
test.log('Skipping :root test due to jsdom (from isNode)');
|
||||
test.log('Skipping :root test due to jsdom');
|
||||
}
|
||||
|
||||
update({ typed: 'tse #', cursor: { start: 5, end: 5 } });
|
||||
@ -2360,7 +2350,7 @@ exports.testActivate = function(options) {
|
||||
|
||||
helpers.setInput('tsg d');
|
||||
helpers.check({
|
||||
hints: ' [options]'
|
||||
hints: ' [options] -> ccc'
|
||||
});
|
||||
|
||||
helpers.setInput('tsg aa');
|
||||
@ -2774,9 +2764,10 @@ var mockDoc = {
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
define('gclitest/testFocus', ['require', 'exports', 'module' , 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
|
||||
define('gclitest/testFocus', ['require', 'exports', 'module' , 'test/assert', 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
|
||||
|
||||
|
||||
var test = require('test/assert');
|
||||
var helpers = require('gclitest/helpers');
|
||||
var mockCommands = require('gclitest/mockCommands');
|
||||
|
||||
@ -2791,6 +2782,11 @@ exports.shutdown = function(options) {
|
||||
};
|
||||
|
||||
exports.testBasic = function(options) {
|
||||
if (options.isJsdom) {
|
||||
test.log('jsdom does not pass on focus events properly, skipping testBasic');
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.focusInput();
|
||||
helpers.exec(options, 'help');
|
||||
|
||||
@ -3343,9 +3339,8 @@ exports.testHidden = function(options) {
|
||||
helpers.setInput('tshidde');
|
||||
helpers.check({
|
||||
input: 'tshidde',
|
||||
markup: 'EEEEEEE',
|
||||
status: 'ERROR',
|
||||
hints: '',
|
||||
hints: ' -> tse',
|
||||
status: 'ERROR'
|
||||
});
|
||||
|
||||
helpers.setInput('tshidden');
|
||||
@ -3470,10 +3465,11 @@ exports.testHidden = function(options) {
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert'], function(require, exports, module) {
|
||||
define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert', 'gcli/canon'], function(require, exports, module) {
|
||||
|
||||
var helpers = require('gclitest/helpers');
|
||||
var test = require('test/assert');
|
||||
var canon = require('gcli/canon');
|
||||
|
||||
exports.setup = function(options) {
|
||||
helpers.setup(options);
|
||||
@ -3484,8 +3480,8 @@ define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers
|
||||
};
|
||||
|
||||
exports.testIntroStatus = function(options) {
|
||||
if (options.isFirefox) {
|
||||
test.log('Skipping testIntroStatus in Firefox.');
|
||||
if (canon.getCommand('intro') == null) {
|
||||
test.log('Skipping testIntroStatus; missing intro command.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3507,8 +3503,8 @@ define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers
|
||||
};
|
||||
|
||||
exports.testIntroExec = function(options) {
|
||||
if (options.isFirefox) {
|
||||
test.log('Skipping testIntroExec in Firefox.');
|
||||
if (canon.getCommand('intro') == null) {
|
||||
test.log('Skipping testIntroStatus; missing intro command.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3683,7 +3679,10 @@ exports.testBasic = function(options) {
|
||||
input('{ document.title');
|
||||
check('VVVVVVVVVVVVVVVV', Status.VALID, 'document.title', 0);
|
||||
|
||||
test.ok('donteval' in options.window, 'donteval exists');
|
||||
if (!options.isJsdom) {
|
||||
// jsdom causes an eval here, maybe that's node/v8?
|
||||
test.ok('donteval' in options.window, 'donteval exists');
|
||||
}
|
||||
|
||||
input('{ don');
|
||||
check('VVIII', Status.ERROR, 'don', 'donteval');
|
||||
@ -3832,12 +3831,12 @@ exports.testComplete = function(options) {
|
||||
check('{ wind', COMPLETES_TO, '{ window', 0);
|
||||
check('{ window.docum', COMPLETES_TO, '{ window.document', 0);
|
||||
|
||||
// Bug 717228: This fails under node
|
||||
if (!options.isNode) {
|
||||
// Bug 717228: This fails under jsdom
|
||||
if (!options.isJsdom) {
|
||||
check('{ window.document.titl', COMPLETES_TO, '{ window.document.title ', 0);
|
||||
}
|
||||
else {
|
||||
test.log('Running under Node. Skipping tests due to bug 717228.');
|
||||
test.log('Skipping tests due to jsdom and bug 717228.');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -4008,61 +4007,66 @@ exports.testNode = function(options) {
|
||||
}
|
||||
});
|
||||
|
||||
helpers.setInput('tse :root');
|
||||
helpers.check({
|
||||
input: 'tse :root',
|
||||
hints: ' [options]',
|
||||
markup: 'VVVVVVVVV',
|
||||
cursor: 9,
|
||||
current: 'node',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: { arg: ' :root', status: 'VALID' },
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
if (options.isJsdom) {
|
||||
test.log('skipping node tests because jsdom');
|
||||
}
|
||||
else {
|
||||
helpers.setInput('tse :root');
|
||||
helpers.check({
|
||||
input: 'tse :root',
|
||||
hints: ' [options]',
|
||||
markup: 'VVVVVVVVV',
|
||||
cursor: 9,
|
||||
current: 'node',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: { arg: ' :root', status: 'VALID' },
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
|
||||
helpers.setInput('tse :root ');
|
||||
helpers.check({
|
||||
input: 'tse :root ',
|
||||
hints: '[options]',
|
||||
markup: 'VVVVVVVVVV',
|
||||
cursor: 10,
|
||||
current: 'node',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: { arg: ' :root ', status: 'VALID' },
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
test.is(requisition.getAssignment('node').value.tagName,
|
||||
'HTML',
|
||||
'root id');
|
||||
helpers.setInput('tse :root ');
|
||||
helpers.check({
|
||||
input: 'tse :root ',
|
||||
hints: '[options]',
|
||||
markup: 'VVVVVVVVVV',
|
||||
cursor: 10,
|
||||
current: 'node',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: { arg: ' :root ', status: 'VALID' },
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
test.is(requisition.getAssignment('node').value.tagName,
|
||||
'HTML',
|
||||
'root id');
|
||||
|
||||
helpers.setInput('tse #gcli-nomatch');
|
||||
helpers.check({
|
||||
input: 'tse #gcli-nomatch',
|
||||
hints: ' [options]',
|
||||
markup: 'VVVVIIIIIIIIIIIII',
|
||||
cursor: 17,
|
||||
current: 'node',
|
||||
status: 'ERROR',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: {
|
||||
value: undefined,
|
||||
arg: ' #gcli-nomatch',
|
||||
status: 'INCOMPLETE',
|
||||
message: 'No matches'
|
||||
},
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
helpers.setInput('tse #gcli-nomatch');
|
||||
helpers.check({
|
||||
input: 'tse #gcli-nomatch',
|
||||
hints: ' [options]',
|
||||
markup: 'VVVVIIIIIIIIIIIII',
|
||||
cursor: 17,
|
||||
current: 'node',
|
||||
status: 'ERROR',
|
||||
args: {
|
||||
command: { name: 'tse' },
|
||||
node: {
|
||||
value: undefined,
|
||||
arg: ' #gcli-nomatch',
|
||||
status: 'INCOMPLETE',
|
||||
message: 'No matches'
|
||||
},
|
||||
nodes: { status: 'VALID' },
|
||||
nodes2: { status: 'VALID' }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
helpers.setInput('tse #');
|
||||
helpers.check({
|
||||
@ -4131,6 +4135,11 @@ exports.testNode = function(options) {
|
||||
exports.testNodes = function(options) {
|
||||
var requisition = options.display.requisition;
|
||||
|
||||
if (options.isJsdom) {
|
||||
test.log('skipping node tests because jsdom');
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.setInput('tse :root --nodes *');
|
||||
helpers.check({
|
||||
input: 'tse :root --nodes *',
|
||||
@ -4236,13 +4245,14 @@ exports.testNodes = function(options) {
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert'], function(require, exports, module) {
|
||||
define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert', 'gcli/canon'], function(require, exports, module) {
|
||||
|
||||
|
||||
var pref = require('gcli/commands/pref');
|
||||
var helpers = require('gclitest/helpers');
|
||||
var mockSettings = require('gclitest/mockSettings');
|
||||
var test = require('test/assert');
|
||||
var canon = require('gcli/canon');
|
||||
|
||||
|
||||
exports.setup = function(options) {
|
||||
@ -4270,6 +4280,11 @@ exports.testPrefShowStatus = function(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canon.getCommand('intro') == null) {
|
||||
test.log('Skipping testIntroStatus; missing intro command.');
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.setInput('pref s');
|
||||
helpers.check({
|
||||
typed: 'pref s',
|
||||
@ -4333,6 +4348,11 @@ exports.testPrefSetStatus = function(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canon.getCommand('intro') == null) {
|
||||
test.log('Skipping testIntroStatus; missing intro command.');
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.setInput('pref s');
|
||||
helpers.check({
|
||||
typed: 'pref s',
|
||||
@ -4352,7 +4372,7 @@ exports.testPrefSetStatus = function(options) {
|
||||
helpers.setInput('pref xxx');
|
||||
helpers.check({
|
||||
typed: 'pref xxx',
|
||||
markup: 'EEEEVEEE',
|
||||
markup: 'IIIIVIII',
|
||||
status: 'ERROR'
|
||||
});
|
||||
|
||||
@ -4395,6 +4415,11 @@ exports.testPrefExec = function(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canon.getCommand('intro') == null) {
|
||||
test.log('Skipping testIntroStatus; missing intro command.');
|
||||
return;
|
||||
}
|
||||
|
||||
var initialAllowSet = pref.allowSet.value;
|
||||
pref.allowSet.value = false;
|
||||
|
||||
@ -4744,12 +4769,11 @@ exports.testPredictions = function(options) {
|
||||
var resource3 = types.getType({ name: 'resource', include: 'text/css' });
|
||||
var options3 = resource3.getLookup();
|
||||
// jsdom fails to support digging into stylesheets
|
||||
if (!options.isNode) {
|
||||
if (!options.isJsdom) {
|
||||
test.ok(options3.length >= 1, 'have resources');
|
||||
}
|
||||
else {
|
||||
test.log('Running under Node. ' +
|
||||
'Skipping checks due to jsdom document.stylsheets support.');
|
||||
test.log('Skipping checks due to jsdom document.stylsheets support.');
|
||||
}
|
||||
options3.forEach(function(prediction) {
|
||||
checkPrediction(resource3, prediction);
|
||||
@ -5018,7 +5042,7 @@ exports.testChange = function(options) {
|
||||
define('gclitest/testSpell', ['require', 'exports', 'module' , 'test/assert', 'gcli/types/spell'], function(require, exports, module) {
|
||||
|
||||
var test = require('test/assert');
|
||||
var Speller = require('gcli/types/spell').Speller;
|
||||
var spell = require('gcli/types/spell');
|
||||
|
||||
exports.setup = function() {
|
||||
};
|
||||
@ -5032,15 +5056,14 @@ exports.testSpellerSimple = function(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
var speller = new Speller();
|
||||
speller.train(Object.keys(options.window));
|
||||
var alternatives = Object.keys(options.window);
|
||||
|
||||
test.is(speller.correct('document'), 'document');
|
||||
test.is(speller.correct('documen'), 'document');
|
||||
test.is(speller.correct('ocument'), 'document');
|
||||
test.is(speller.correct('odcument'), 'document');
|
||||
test.is(spell.correct('document', alternatives), 'document');
|
||||
test.is(spell.correct('documen', alternatives), 'document');
|
||||
test.is(spell.correct('ocument', alternatives), 'document');
|
||||
test.is(spell.correct('odcument', alternatives), 'document');
|
||||
|
||||
test.is(speller.correct('========='), null);
|
||||
test.is(spell.correct('=========', alternatives), undefined);
|
||||
};
|
||||
|
||||
|
||||
@ -5458,7 +5481,7 @@ function type(typed, tests, options) {
|
||||
inputter.setCursor({ start: tests.cursor, end: tests.cursor });
|
||||
}
|
||||
|
||||
if (!options.isNode) {
|
||||
if (!options.isJsdom) {
|
||||
if (tests.important) {
|
||||
test.ok(tooltip.field.isImportant, 'Important for ' + typed);
|
||||
}
|
||||
@ -5488,8 +5511,8 @@ exports.testActivate = function(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.isNode) {
|
||||
test.log('Running under Node. Reduced checks due to JSDom.textContent');
|
||||
if (options.isJsdom) {
|
||||
test.log('Reduced checks due to JSDom.textContent');
|
||||
}
|
||||
|
||||
type(' ', { }, options);
|
||||
@ -5506,11 +5529,9 @@ exports.testActivate = function(options) {
|
||||
|
||||
type('tsb tt', {
|
||||
important: true,
|
||||
options: [ ],
|
||||
error: 'Can\'t use \'tt\'.'
|
||||
options: [ 'true' ]
|
||||
}, options);
|
||||
|
||||
|
||||
type('asdf', {
|
||||
important: false,
|
||||
options: [ ],
|
||||
@ -5572,9 +5593,8 @@ function forEachType(options, callback) {
|
||||
}
|
||||
|
||||
exports.testDefault = function(options) {
|
||||
if (options.isNode) {
|
||||
test.log('Running under Node. ' +
|
||||
'Skipping tests due to issues with resource type.');
|
||||
if (options.isJsdom) {
|
||||
test.log('Skipping tests due to issues with resource type.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
|
||||
const BUTTON_POSITION_SAVE = 0;
|
||||
const BUTTON_POSITION_CANCEL = 1;
|
||||
const BUTTON_POSITION_DONT_SAVE = 2;
|
||||
const BUTTON_POSITION_REVERT=0;
|
||||
|
||||
let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
|
||||
|
||||
@ -922,6 +923,72 @@ var Scratchpad = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore content from saved version of current file.
|
||||
*
|
||||
* @param function aCallback
|
||||
* Optional function you want to call when file is saved
|
||||
*/
|
||||
revertFile: function SP_revertFile(aCallback)
|
||||
{
|
||||
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this.filename);
|
||||
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.importFromFile(file, false, function(aStatus, aContent) {
|
||||
if (aCallback) {
|
||||
aCallback(aStatus);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt to revert scratchpad if it has unsaved changes.
|
||||
*
|
||||
* @param function aCallback
|
||||
* Optional function you want to call when file is saved. The callback
|
||||
* receives three arguments:
|
||||
* - aRevert (boolean) - tells if the file has been reverted.
|
||||
* - status (number) - the file revert status result (if the file was
|
||||
* saved).
|
||||
*/
|
||||
promptRevert: function SP_promptRervert(aCallback)
|
||||
{
|
||||
if (this.filename) {
|
||||
let ps = Services.prompt;
|
||||
let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_REVERT +
|
||||
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
|
||||
|
||||
let button = ps.confirmEx(window,
|
||||
this.strings.GetStringFromName("confirmRevert.title"),
|
||||
this.strings.GetStringFromName("confirmRevert"),
|
||||
flags, null, null, null, null, {});
|
||||
if (button == BUTTON_POSITION_CANCEL) {
|
||||
if (aCallback) {
|
||||
aCallback(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (button == BUTTON_POSITION_REVERT) {
|
||||
this.revertFile(function(aStatus) {
|
||||
if(aCallback){
|
||||
aCallback(true, aStatus);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the Error Console.
|
||||
*/
|
||||
@ -1107,6 +1174,14 @@ var Scratchpad = {
|
||||
_onDirtyChanged: function SP__onDirtyChanged(aEvent)
|
||||
{
|
||||
Scratchpad._updateTitle();
|
||||
if (Scratchpad.filename) {
|
||||
if (Scratchpad.editor.dirty) {
|
||||
document.getElementById("sp-cmd-revert").removeAttribute("disabled");
|
||||
}
|
||||
else {
|
||||
document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,7 @@
|
||||
<command id="sp-cmd-clearRecentFiles" oncommand="Scratchpad.clearRecentFiles();"/>
|
||||
<command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
|
||||
<command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
|
||||
<command id="sp-cmd-revert" oncommand="Scratchpad.promptRevert();" disabled="true"/>
|
||||
|
||||
<!-- TODO: bug 650340 - implement printFile()
|
||||
<command id="sp-cmd-printFile" oncommand="Scratchpad.printFile();" disabled="true"/>
|
||||
@ -132,6 +133,10 @@
|
||||
label="&saveFileAsCmd.label;"
|
||||
accesskey="&saveFileAsCmd.accesskey;"
|
||||
command="sp-cmd-saveas"/>
|
||||
<menuitem id="sp-menu-revert"
|
||||
label="&revertCmd.label;"
|
||||
accesskey="&revertCmd.accesskey;"
|
||||
command="sp-cmd-revert"/>
|
||||
<menuseparator/>
|
||||
|
||||
<!-- TODO: bug 650340 - implement printFile
|
||||
|
@ -33,6 +33,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_scratchpad_bug_650760_help_key.js \
|
||||
browser_scratchpad_bug_651942_recent_files.js \
|
||||
browser_scratchpad_bug756681_display_non_error_exceptions.js \
|
||||
browser_scratchpad_bug_751744_revert_to_saved.js \
|
||||
head.js \
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -0,0 +1,137 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let NetUtil = tempScope.NetUtil;
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
// Reference to the Scratchpad object.
|
||||
let gScratchpad;
|
||||
|
||||
// Reference to the temporary nsIFiles.
|
||||
let gFile;
|
||||
|
||||
// Temporary file name.
|
||||
let gFileName = "testFileForBug751744.tmp"
|
||||
|
||||
|
||||
// Content for the temporary file.
|
||||
let gFileContent = "/* this file is already saved */\n" +
|
||||
"function foo() { alert('bar') }";
|
||||
let gLength = gFileContent.length;
|
||||
|
||||
// Reference to the menu entry.
|
||||
let menu;
|
||||
|
||||
function startTest()
|
||||
{
|
||||
gScratchpad = gScratchpadWindow.Scratchpad;
|
||||
menu = gScratchpadWindow.document.getElementById("sp-menu-revert");
|
||||
createAndLoadTemporaryFile();
|
||||
}
|
||||
|
||||
function testAfterSaved() {
|
||||
// Check if the revert menu is disabled as the file is at saved state.
|
||||
ok(menu.hasAttribute("disabled"), "The revert menu entry is disabled.");
|
||||
|
||||
// chancging the text in the file
|
||||
gScratchpad.setText("\nfoo();", gLength, gLength);
|
||||
// Checking the text got changed
|
||||
is(gScratchpad.getText(), gFileContent + "\nfoo();",
|
||||
"The text changed the first time.");
|
||||
|
||||
// Revert menu now should be enabled.
|
||||
ok(!menu.hasAttribute("disabled"),
|
||||
"The revert menu entry is enabled after changing text first time");
|
||||
|
||||
// reverting back to last saved state.
|
||||
gScratchpad.revertFile(testAfterRevert);
|
||||
}
|
||||
|
||||
function testAfterRevert() {
|
||||
// Check if the file's text got reverted
|
||||
is(gScratchpad.getText(), gFileContent,
|
||||
"The text reverted back to original text.");
|
||||
// The revert menu should be disabled again.
|
||||
ok(menu.hasAttribute("disabled"),
|
||||
"The revert menu entry is disabled after reverting.");
|
||||
|
||||
// chancging the text in the file again
|
||||
gScratchpad.setText("\nalert(foo.toSource());", gLength, gLength);
|
||||
// Saving the file.
|
||||
gScratchpad.saveFile(testAfterSecondSave);
|
||||
}
|
||||
|
||||
function testAfterSecondSave() {
|
||||
// revert menu entry should be disabled.
|
||||
ok(menu.hasAttribute("disabled"),
|
||||
"The revert menu entry is disabled after saving.");
|
||||
|
||||
// changing the text.
|
||||
gScratchpad.setText("\nfoo();", gLength + 23, gLength + 23);
|
||||
|
||||
// revert menu entry should get enabled yet again.
|
||||
ok(!menu.hasAttribute("disabled"),
|
||||
"The revert menu entry is enabled after changing text third time");
|
||||
|
||||
// reverting back to last saved state.
|
||||
gScratchpad.revertFile(testAfterSecondRevert);
|
||||
}
|
||||
|
||||
function testAfterSecondRevert() {
|
||||
// Check if the file's text got reverted
|
||||
is(gScratchpad.getText(), gFileContent + "\nalert(foo.toSource());",
|
||||
"The text reverted back to the changed saved text.");
|
||||
// The revert menu should be disabled again.
|
||||
ok(menu.hasAttribute("disabled"),
|
||||
"Revert menu entry is disabled after reverting to changed saved state.");
|
||||
gFile.remove(false);
|
||||
gFile = null;
|
||||
gScratchpad = null;
|
||||
}
|
||||
|
||||
function createAndLoadTemporaryFile()
|
||||
{
|
||||
// Create a temporary file.
|
||||
gFile = FileUtils.getFile("TmpD", [gFileName]);
|
||||
gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
|
||||
// Write the temporary file.
|
||||
let fout = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
|
||||
0644, fout.DEFER_OPEN);
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let fileContentStream = converter.convertToInputStream(gFileContent);
|
||||
|
||||
NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
|
||||
}
|
||||
|
||||
function tempFileSaved(aStatus)
|
||||
{
|
||||
ok(Components.isSuccessCode(aStatus),
|
||||
"the temporary file was saved successfully");
|
||||
|
||||
// Import the file into Scratchpad.
|
||||
gScratchpad.setFilename(gFile.path);
|
||||
gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile), true,
|
||||
testAfterSaved);
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
openScratchpad(startTest);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<p>test reverting to last saved state of" +
|
||||
" a file </p>";
|
||||
}
|
@ -15,6 +15,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource:///modules/devtools/Commands.jsm");
|
||||
|
||||
const Node = Components.interfaces.nsIDOMNode;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
@ -137,6 +139,28 @@ DeveloperToolbar.prototype.focus = function DT_focus()
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from browser.xul in response to menu-click or keyboard shortcut to
|
||||
* toggle the toolbar
|
||||
*/
|
||||
DeveloperToolbar.prototype.focusToggle = function DT_focusToggle()
|
||||
{
|
||||
if (this.visible) {
|
||||
// If we have focus then the active element is the HTML input contained
|
||||
// inside the xul input element
|
||||
var active = this._chromeWindow.document.activeElement;
|
||||
var position = this._input.compareDocumentPosition(active);
|
||||
if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
||||
this.hide();
|
||||
}
|
||||
else {
|
||||
this._input.focus();
|
||||
}
|
||||
} else {
|
||||
this.show(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Even if the user has not clicked on 'Got it' in the intro, we only show it
|
||||
* once per session.
|
||||
|
@ -758,6 +758,87 @@ StyleEditor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Decode a CSS source string to unicode according to the character set rules
|
||||
* defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
|
||||
*
|
||||
* @param string aString
|
||||
* Source of a CSS stylesheet, loaded from file or cache.
|
||||
* @param string aChannelCharset
|
||||
* Charset of the source string if set by the HTTP channel.
|
||||
* @return string
|
||||
* The CSS string, in unicode.
|
||||
*/
|
||||
_decodeCSSCharset: function SE__decodeCSSCharset(aString, aChannelCharset)
|
||||
{
|
||||
// StyleSheet's charset can be specified from multiple sources
|
||||
|
||||
if (aChannelCharset.length > 0) {
|
||||
// step 1 of syndata.html: charset given in HTTP header.
|
||||
return this._convertToUnicode(aString, aChannelCharset);
|
||||
}
|
||||
|
||||
let sheet = this.styleSheet;
|
||||
if (sheet) {
|
||||
// Do we have a @charset rule in the stylesheet?
|
||||
// step 2 of syndata.html (without the BOM check).
|
||||
if (sheet.cssRules) {
|
||||
let rules = sheet.cssRules;
|
||||
if (rules.length
|
||||
&& rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
|
||||
return this._convertToUnicode(aString, rules.item(0).encoding);
|
||||
}
|
||||
}
|
||||
|
||||
if (sheet.ownerNode) {
|
||||
// step 3: see <link charset="…">
|
||||
let linkCharset = sheet.ownerNode.getAttribute("charset");
|
||||
if (linkCharset != null) {
|
||||
return this._convertToUnicode(aString, linkCharset);
|
||||
}
|
||||
}
|
||||
|
||||
// step 4 (1 of 2): charset of referring stylesheet.
|
||||
let parentSheet = sheet.parentStyleSheet;
|
||||
if (parentSheet && parentSheet.cssRules &&
|
||||
parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
|
||||
return this._convertToUnicode(aString,
|
||||
parentSheet.cssRules[0].encoding);
|
||||
}
|
||||
|
||||
// step 4 (2 of 2): charset of referring document.
|
||||
if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
|
||||
return this._convertToUnicode(aString,
|
||||
sheet.ownerNode.ownerDocument.characterSet);
|
||||
}
|
||||
}
|
||||
|
||||
// step 5: default to utf-8.
|
||||
return this._convertToUnicode(aString, "UTF-8");
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a given string, encoded in a given character set, to unicode.
|
||||
* @param string aString
|
||||
* A string.
|
||||
* @param string aCharset
|
||||
* A character set.
|
||||
* @return string
|
||||
* A unicode string.
|
||||
*/
|
||||
_convertToUnicode: function SE__convertToUnicode(aString, aCharset) {
|
||||
// Decoding primitives.
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
|
||||
try {
|
||||
converter.charset = aCharset;
|
||||
return converter.ConvertToUnicode(aString);
|
||||
} catch(e) {
|
||||
return aString;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load source from a file or file-like resource.
|
||||
*
|
||||
@ -790,6 +871,7 @@ StyleEditor.prototype = {
|
||||
{
|
||||
let channel = Services.io.newChannel(aHref, null, null);
|
||||
let chunks = [];
|
||||
let channelCharset = "";
|
||||
let streamListener = { // nsIStreamListener inherits nsIRequestObserver
|
||||
onStartRequest: function (aRequest, aContext, aStatusCode) {
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
@ -797,6 +879,10 @@ StyleEditor.prototype = {
|
||||
}
|
||||
}.bind(this),
|
||||
onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
|
||||
let channel = aRequest.QueryInterface(Ci.nsIChannel);
|
||||
if (!channelCharset) {
|
||||
channelCharset = channel.contentCharset;
|
||||
}
|
||||
chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
|
||||
},
|
||||
onStopRequest: function (aRequest, aContext, aStatusCode) {
|
||||
@ -804,7 +890,7 @@ StyleEditor.prototype = {
|
||||
return this._signalError(LOAD_ERROR);
|
||||
}
|
||||
|
||||
this._onSourceLoad(chunks.join(""));
|
||||
this._onSourceLoad(chunks.join(""), channelCharset);
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
@ -816,9 +902,14 @@ StyleEditor.prototype = {
|
||||
* Called when source has been loaded.
|
||||
*
|
||||
* @param string aSourceText
|
||||
* @param string aCharset
|
||||
* Optional. The character set to use. The default is to detect the
|
||||
* character set following the standard (see
|
||||
* <http://www.w3.org/TR/CSS2/syndata.html#charset>).
|
||||
*/
|
||||
_onSourceLoad: function SE__onSourceLoad(aSourceText)
|
||||
_onSourceLoad: function SE__onSourceLoad(aSourceText, aCharset)
|
||||
{
|
||||
aSourceText = this._decodeCSSCharset(aSourceText, aCharset || "");
|
||||
this._restoreExpando();
|
||||
this._state.text = prettifyCSS(aSourceText);
|
||||
this._loaded = true;
|
||||
|
@ -71,6 +71,10 @@ function testEditorAdded(aChrome, aEditor)
|
||||
|
||||
function testFirstStyleSheetEditor(aChrome, aEditor)
|
||||
{
|
||||
// Note: the html <link> contains charset="UTF-8".
|
||||
ok(aEditor._state.text.indexOf("\u263a") >= 0,
|
||||
"stylesheet is unicode-aware.");
|
||||
|
||||
//testing TESTCASE's simple.css stylesheet
|
||||
is(aEditor.styleSheetIndex, 0,
|
||||
"first stylesheet is at index 0");
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* ☺ */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>simple testcase</title>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="simple.css"/>
|
||||
<link rel="stylesheet" charset="UTF-8" type="text/css" media="screen" href="simple.css"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: white;
|
||||
|
@ -36,6 +36,9 @@
|
||||
<!ENTITY openRecentMenu.label "Open Recent">
|
||||
<!ENTITY openRecentMenu.accesskey "R">
|
||||
|
||||
<!ENTITY revertCmd.label "Revert…">
|
||||
<!ENTITY revertCmd.accesskey "t">
|
||||
|
||||
<!ENTITY saveFileCmd.label "Save">
|
||||
<!ENTITY saveFileCmd.accesskey "S">
|
||||
<!ENTITY saveFileCmd.commandkey "s">
|
||||
|
@ -54,6 +54,14 @@ confirmClose=Do you want to save the changes you made to this scratchpad?
|
||||
# you try to close a scratchpad with unsaved changes.
|
||||
confirmClose.title=Unsaved Changes
|
||||
|
||||
# LOCALIZATION NOTE (confirmRevert): This is message in the prompt dialog when
|
||||
# you try to revert unsaved content of scratchpad.
|
||||
confirmRevert=Do you want to revert the changes you made to this scratchpad?
|
||||
|
||||
# LOCALIZATION NOTE (confirmRevert.title): This is title of the prompt dialog when
|
||||
# you try to revert unsaved content of scratchpad.
|
||||
confirmRevert.title=Revert Changes
|
||||
|
||||
# LOCALIZATION NOTE (scratchpadIntro): This is a multi-line comment explaining
|
||||
# how to use the Scratchpad. Note that this should be a valid JavaScript
|
||||
# comment inside /* and */.
|
||||
|
@ -3156,6 +3156,14 @@ html|*#gcli-output-frame {
|
||||
0 1px 0 hsla(210,16%,76%,.15);
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node::-moz-selection {
|
||||
background-color: green !important;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.gclitoolbar-complete-node {
|
||||
padding-left: 21px;
|
||||
background-color: transparent;
|
||||
|
@ -3118,6 +3118,10 @@ html|*#gcli-output-frame {
|
||||
0 0 0 1px hsla(210,40%,83%,.1);
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node {
|
||||
background-color: green !important;
|
||||
}
|
||||
|
||||
.gclitoolbar-complete-node {
|
||||
padding-left: 21px;
|
||||
background-color: transparent;
|
||||
|
Loading…
Reference in New Issue
Block a user