zischenstand
This commit is contained in:
105
src/macro_activate-currency-history-tab.js
Normal file
105
src/macro_activate-currency-history-tab.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* Activate currency history tracking that stores entries in flags only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
const CURRENCY_PATH = "system.currency";
|
||||||
|
const FLAG_SCOPE = "world";
|
||||||
|
const FLAG_KEY = "pf1CurrencyHistory";
|
||||||
|
const MAX_ROWS = 50;
|
||||||
|
const COIN_ORDER = ["pp", "gp", "sp", "cp"];
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
game.pf1 ??= {};
|
||||||
|
game.pf1.currencyHistoryFlags ??= { current: {}, hooks: {} };
|
||||||
|
const state = game.pf1.currencyHistoryFlags;
|
||||||
|
|
||||||
|
const logKey = `pf1-currency-history-flags-${ACTOR_ID}`;
|
||||||
|
if (state.hooks[logKey]) {
|
||||||
|
Hooks.off("updateActor", state.hooks[logKey]);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
primeSnapshot(game.actors.get(ACTOR_ID) ?? TARGET_ACTOR);
|
||||||
|
|
||||||
|
state.hooks[logKey] = Hooks.on("updateActor", (actor, change, options, userId) => {
|
||||||
|
if (actor.id !== ACTOR_ID) return;
|
||||||
|
|
||||||
|
const newCurrency = readCurrency(actor);
|
||||||
|
if (!newCurrency) return;
|
||||||
|
|
||||||
|
const previous = state.current[ACTOR_ID];
|
||||||
|
if (!previous) {
|
||||||
|
state.current[ACTOR_ID] = newCurrency;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currencyEquals(previous, newCurrency)) return;
|
||||||
|
|
||||||
|
const diff = diffCurrency(previous, newCurrency);
|
||||||
|
const user = game.users.get(userId);
|
||||||
|
|
||||||
|
state.current[ACTOR_ID] = newCurrency;
|
||||||
|
|
||||||
|
appendHistoryEntry(actor, {
|
||||||
|
value: formatCurrency(newCurrency),
|
||||||
|
diff: formatCurrency(diff, true),
|
||||||
|
user: user?.name ?? "System",
|
||||||
|
}).catch((err) => console.error("Currency History Flags | Failed to append entry", err));
|
||||||
|
});
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`Currency flag history tracking active for ${actorName}.`);
|
||||||
|
|
||||||
|
async function appendHistoryEntry(actor, entry) {
|
||||||
|
const existing = (await actor.getFlag(FLAG_SCOPE, FLAG_KEY)) ?? [];
|
||||||
|
existing.unshift({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: entry.value,
|
||||||
|
diff: entry.diff,
|
||||||
|
user: entry.user,
|
||||||
|
});
|
||||||
|
if (existing.length > MAX_ROWS) existing.splice(MAX_ROWS);
|
||||||
|
|
||||||
|
await actor.setFlag(FLAG_SCOPE, FLAG_KEY, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function primeSnapshot(actor) {
|
||||||
|
const currency = readCurrency(actor);
|
||||||
|
if (!currency) return;
|
||||||
|
state.current[ACTOR_ID] = currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readCurrency(actor) {
|
||||||
|
if (!actor) return null;
|
||||||
|
const base = foundry.utils.getProperty(actor, CURRENCY_PATH) ?? actor.system?.currency;
|
||||||
|
if (!base) return null;
|
||||||
|
const clone = {};
|
||||||
|
for (const coin of COIN_ORDER) clone[coin] = Number(base[coin] ?? 0);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
function currencyEquals(a, b) {
|
||||||
|
return COIN_ORDER.every((coin) => (a[coin] ?? 0) === (b[coin] ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function diffCurrency(prev, next) {
|
||||||
|
const diff = {};
|
||||||
|
for (const coin of COIN_ORDER) diff[coin] = (next[coin] ?? 0) - (prev[coin] ?? 0);
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCurrency(obj, skipZero = false) {
|
||||||
|
return COIN_ORDER.map((coin) => obj[coin] ?? 0)
|
||||||
|
.map((value, idx) => {
|
||||||
|
if (skipZero && value === 0) return null;
|
||||||
|
const label = COIN_ORDER[idx];
|
||||||
|
const v = skipZero && value > 0 ? `+${value}` : `${value}`;
|
||||||
|
return `${label}:${v}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
208
src/macro_activate-hp-history-tab.js
Normal file
208
src/macro_activate-hp-history-tab.js
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
* Activate HP history tracking that stores entries in flags only.
|
||||||
|
* Pair with macro_enable-history-tab.js to view the data inside a History tab.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
const HP_PATH = "system.attributes.hp.value";
|
||||||
|
const FLAG_SCOPE = "world";
|
||||||
|
const FLAG_KEY = "pf1HpHistory";
|
||||||
|
const UPDATE_CONTEXT_FLAG = "hpHistoryFlags";
|
||||||
|
const MAX_ROWS = 50;
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
game.pf1 ??= {};
|
||||||
|
game.pf1.hpHistoryFlags ??= { sources: new Map(), current: {}, hooks: {} };
|
||||||
|
const state = game.pf1.hpHistoryFlags;
|
||||||
|
|
||||||
|
const logKey = `pf1-hp-history-flags-${ACTOR_ID}`;
|
||||||
|
if (state.hooks[logKey]) {
|
||||||
|
Hooks.off("updateActor", state.hooks[logKey]);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureDamageSourceTracking();
|
||||||
|
primeSnapshot(game.actors.get(ACTOR_ID) ?? TARGET_ACTOR);
|
||||||
|
|
||||||
|
state.hooks[logKey] = Hooks.on("updateActor", (actor, change, options, userId) => {
|
||||||
|
if (actor.id !== ACTOR_ID) return;
|
||||||
|
if (options?.[UPDATE_CONTEXT_FLAG]) return;
|
||||||
|
|
||||||
|
const newHP = readHpValue(actor);
|
||||||
|
if (newHP === null) return;
|
||||||
|
|
||||||
|
const previous = state.current[ACTOR_ID];
|
||||||
|
if (previous === undefined) {
|
||||||
|
state.current[ACTOR_ID] = newHP;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Object.is(newHP, previous)) return;
|
||||||
|
|
||||||
|
const diff = newHP - previous;
|
||||||
|
const diffText = diff >= 0 ? `+${diff}` : `${diff}`;
|
||||||
|
const user = game.users.get(userId);
|
||||||
|
const source = consumeDamageSource(actor.id) ?? inferManualSource(diff);
|
||||||
|
|
||||||
|
state.current[ACTOR_ID] = newHP;
|
||||||
|
|
||||||
|
appendHistoryEntry(actor, {
|
||||||
|
hp: newHP,
|
||||||
|
diff: diffText,
|
||||||
|
user: user?.name ?? "System",
|
||||||
|
source: source ?? "",
|
||||||
|
}).catch((err) => console.error("HP History Flags | Failed to append entry", err));
|
||||||
|
});
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`HP flag history tracking active for ${actorName}.`);
|
||||||
|
|
||||||
|
async function appendHistoryEntry(actor, entry) {
|
||||||
|
const existing = (await actor.getFlag(FLAG_SCOPE, FLAG_KEY)) ?? [];
|
||||||
|
existing.unshift({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
hp: entry.hp,
|
||||||
|
diff: entry.diff,
|
||||||
|
user: entry.user,
|
||||||
|
source: entry.source,
|
||||||
|
});
|
||||||
|
if (existing.length > MAX_ROWS) existing.splice(MAX_ROWS);
|
||||||
|
|
||||||
|
await actor.setFlag(FLAG_SCOPE, FLAG_KEY, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function consumeDamageSource(actorId) {
|
||||||
|
const entry = state.sources.get(actorId);
|
||||||
|
if (!entry) return null;
|
||||||
|
state.sources.delete(actorId);
|
||||||
|
return entry.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferManualSource(diff) {
|
||||||
|
if (!Number.isFinite(diff) || diff === 0) return null;
|
||||||
|
return diff > 0 ? "Manual healing" : "Manual damage";
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDamageSourceTracking() {
|
||||||
|
if (state.applyDamageWrapped) return;
|
||||||
|
|
||||||
|
const ActorPF = pf1?.documents?.actor?.ActorPF;
|
||||||
|
if (!ActorPF?.applyDamage) return;
|
||||||
|
|
||||||
|
const original = ActorPF.applyDamage;
|
||||||
|
ActorPF.applyDamage = async function wrappedApplyDamage(value, options = {}) {
|
||||||
|
try {
|
||||||
|
noteDamageSource(value, options);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("HP History Flags | Failed to record source", err);
|
||||||
|
}
|
||||||
|
return original.call(this, value, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
state.applyDamageWrapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function noteDamageSource(value, options) {
|
||||||
|
const actors = resolveActorTargets(options?.targets);
|
||||||
|
if (!actors.some((a) => a?.id === ACTOR_ID)) return;
|
||||||
|
|
||||||
|
const label = buildSourceLabel(value, options);
|
||||||
|
if (!label) return;
|
||||||
|
state.sources.set(ACTOR_ID, { label, ts: Date.now() });
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSourceLabel(value, options = {}) {
|
||||||
|
const info = options.message?.flags?.pf1?.identifiedInfo ?? {};
|
||||||
|
const metadata = options.message?.flags?.pf1?.metadata ?? {};
|
||||||
|
const fromChatFlavor = options.message?.flavor?.trim();
|
||||||
|
|
||||||
|
const actorDoc = resolveActorFromMetadata(metadata);
|
||||||
|
const itemDoc = actorDoc ? resolveItemFromMetadata(actorDoc, metadata) : null;
|
||||||
|
const actorName = actorDoc?.name ?? options.message?.speaker?.alias ?? null;
|
||||||
|
const actionName = info.actionName ?? info.name ?? itemDoc?.name ?? fromChatFlavor ?? null;
|
||||||
|
|
||||||
|
let label = null;
|
||||||
|
if (actorName && actionName) label = `${actorName} -> ${actionName}`;
|
||||||
|
else if (actionName) label = actionName;
|
||||||
|
else if (actorName) label = actorName;
|
||||||
|
else label = value < 0 ? "Healing" : "Damage";
|
||||||
|
|
||||||
|
if (options.isCritical) label += " (Critical)";
|
||||||
|
if (options.asNonlethal) label += " [Nonlethal]";
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
function primeSnapshot(actor) {
|
||||||
|
const hp = readHpValue(actor);
|
||||||
|
if (hp === null) return;
|
||||||
|
state.current[ACTOR_ID] = hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveActorTargets(targets) {
|
||||||
|
let list = [];
|
||||||
|
if (Array.isArray(targets) && targets.length) list = targets;
|
||||||
|
else list = canvas?.tokens?.controlled ?? [];
|
||||||
|
|
||||||
|
return list
|
||||||
|
.map((entry) => {
|
||||||
|
if (!entry) return null;
|
||||||
|
if (entry instanceof Actor) return entry;
|
||||||
|
if (entry.actor) return entry.actor;
|
||||||
|
if (entry.document?.actor) return entry.document.actor;
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((actor) => actor instanceof Actor && actor.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readHpValue(actor) {
|
||||||
|
if (!actor) return null;
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
HP_PATH,
|
||||||
|
HP_PATH.replace(/^system\./, "data."),
|
||||||
|
HP_PATH.replace(/^system\./, ""),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const path of candidates) {
|
||||||
|
const value = foundry.utils.getProperty(actor, path);
|
||||||
|
if (value !== undefined) {
|
||||||
|
const numeric = Number(value);
|
||||||
|
return Number.isFinite(numeric) ? numeric : null;
|
||||||
|
}
|
||||||
|
if (actor.system) {
|
||||||
|
const trimmed = path.startsWith("system.") ? path.slice(7) : path;
|
||||||
|
const systemValue = foundry.utils.getProperty(actor.system, trimmed);
|
||||||
|
if (systemValue !== undefined) {
|
||||||
|
const numeric = Number(systemValue);
|
||||||
|
return Number.isFinite(numeric) ? numeric : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveActorFromMetadata(metadata = {}) {
|
||||||
|
if (!metadata.actor) return null;
|
||||||
|
|
||||||
|
if (typeof fromUuidSync === "function") {
|
||||||
|
try {
|
||||||
|
const doc = fromUuidSync(metadata.actor);
|
||||||
|
if (doc instanceof Actor) return doc;
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("HP History Flags | Failed to resolve actor UUID", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = metadata.actor.split(".").pop();
|
||||||
|
return game.actors.get(id) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveItemFromMetadata(actor, metadata = {}) {
|
||||||
|
if (!metadata.item || !(actor?.items instanceof Collection)) return null;
|
||||||
|
return actor.items.get(metadata.item) ?? null;
|
||||||
|
}
|
||||||
89
src/macro_activate-xp-history-tab.js
Normal file
89
src/macro_activate-xp-history-tab.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Activate XP history tracking that stores entries in flags only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
const XP_PATH = "system.details.xp.value";
|
||||||
|
const FLAG_SCOPE = "world";
|
||||||
|
const FLAG_KEY = "pf1XpHistory";
|
||||||
|
const MAX_ROWS = 50;
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
game.pf1 ??= {};
|
||||||
|
game.pf1.xpHistoryFlags ??= { current: {}, hooks: {} };
|
||||||
|
const state = game.pf1.xpHistoryFlags;
|
||||||
|
|
||||||
|
const logKey = `pf1-xp-history-flags-${ACTOR_ID}`;
|
||||||
|
if (state.hooks[logKey]) {
|
||||||
|
Hooks.off("updateActor", state.hooks[logKey]);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
primeSnapshot(game.actors.get(ACTOR_ID) ?? TARGET_ACTOR);
|
||||||
|
|
||||||
|
state.hooks[logKey] = Hooks.on("updateActor", (actor, change, options, userId) => {
|
||||||
|
if (actor.id !== ACTOR_ID) return;
|
||||||
|
|
||||||
|
const newXP = readXpValue(actor);
|
||||||
|
if (newXP === null) return;
|
||||||
|
|
||||||
|
const previous = state.current[ACTOR_ID];
|
||||||
|
if (previous === undefined) {
|
||||||
|
state.current[ACTOR_ID] = newXP;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Object.is(newXP, previous)) return;
|
||||||
|
|
||||||
|
const diff = newXP - previous;
|
||||||
|
const diffText = diff >= 0 ? `+${diff}` : `${diff}`;
|
||||||
|
const user = game.users.get(userId);
|
||||||
|
|
||||||
|
state.current[ACTOR_ID] = newXP;
|
||||||
|
|
||||||
|
appendHistoryEntry(actor, {
|
||||||
|
value: newXP,
|
||||||
|
diff: diffText,
|
||||||
|
user: user?.name ?? "System",
|
||||||
|
}).catch((err) => console.error("XP History Flags | Failed to append entry", err));
|
||||||
|
});
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`XP flag history tracking active for ${actorName}.`);
|
||||||
|
|
||||||
|
async function appendHistoryEntry(actor, entry) {
|
||||||
|
const existing = (await actor.getFlag(FLAG_SCOPE, FLAG_KEY)) ?? [];
|
||||||
|
existing.unshift({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: entry.value,
|
||||||
|
diff: entry.diff,
|
||||||
|
user: entry.user,
|
||||||
|
});
|
||||||
|
if (existing.length > MAX_ROWS) existing.splice(MAX_ROWS);
|
||||||
|
|
||||||
|
await actor.setFlag(FLAG_SCOPE, FLAG_KEY, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function primeSnapshot(actor) {
|
||||||
|
const xp = readXpValue(actor);
|
||||||
|
if (xp === null) return;
|
||||||
|
state.current[ACTOR_ID] = xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readXpValue(actor) {
|
||||||
|
if (!actor) return null;
|
||||||
|
const direct = foundry.utils.getProperty(actor, XP_PATH);
|
||||||
|
if (direct !== undefined) {
|
||||||
|
const numeric = Number(direct);
|
||||||
|
return Number.isFinite(numeric) ? numeric : null;
|
||||||
|
}
|
||||||
|
const sys = foundry.utils.getProperty(actor.system ?? {}, XP_PATH.replace(/^system\./, ""));
|
||||||
|
if (sys !== undefined) {
|
||||||
|
const numeric = Number(sys);
|
||||||
|
return Number.isFinite(numeric) ? numeric : null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
38
src/macro_disable-history-dialog.js
Normal file
38
src/macro_disable-history-dialog.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Remove the HP history button hooks added by macro_enable-history-dialog.js.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SHEET_EVENTS = [
|
||||||
|
"renderActorSheetPFCharacter",
|
||||||
|
"renderActorSheetPFNPC",
|
||||||
|
"renderActorSheetPFNPCLoot",
|
||||||
|
"renderActorSheetPFNPCLite",
|
||||||
|
"renderActorSheetPFTrap",
|
||||||
|
"renderActorSheetPFVehicle",
|
||||||
|
"renderActorSheetPFHaunt",
|
||||||
|
"renderActorSheetPFBasic",
|
||||||
|
];
|
||||||
|
|
||||||
|
const hooks = game.pf1?.historyDialog?.renderHooks ?? [];
|
||||||
|
if (!hooks.length) {
|
||||||
|
return ui.notifications.info("History dialog hooks are not active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.forEach((hook, idx) => {
|
||||||
|
if (hook == null) return;
|
||||||
|
const isObject = typeof hook === "object";
|
||||||
|
const event = isObject ? hook.event : SHEET_EVENTS[idx];
|
||||||
|
const id = isObject ? hook.id : hook;
|
||||||
|
if (event && id !== undefined) Hooks.off(event, id);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete game.pf1.historyDialog.renderHooks;
|
||||||
|
if (game.pf1?.historyDialog?.observers instanceof Map) {
|
||||||
|
for (const observer of game.pf1.historyDialog.observers.values()) {
|
||||||
|
observer?.disconnect();
|
||||||
|
}
|
||||||
|
game.pf1.historyDialog.observers.clear();
|
||||||
|
}
|
||||||
|
game.pf1.historyDialog.observers = new Map();
|
||||||
|
|
||||||
|
ui.notifications.info("History dialog hooks disabled. Reopen actor sheets to remove the button.");
|
||||||
25
src/macro_disable-history-tab.js
Normal file
25
src/macro_disable-history-tab.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Remove the History tab hooks added by macro_enable-history-tab.js.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const hooks = game.pf1?.historyTab?.renderHooks ?? (game.pf1?.historyTab?.renderHook ? [{ event: null, id: game.pf1.historyTab.renderHook }] : []);
|
||||||
|
if (!hooks.length) {
|
||||||
|
return ui.notifications.info("History tab hooks are not active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const hook of hooks) {
|
||||||
|
if (!hook) continue;
|
||||||
|
Hooks.off(hook.event ?? "renderActorSheet", hook.id ?? hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete game.pf1.historyTab.renderHooks;
|
||||||
|
delete game.pf1.historyTab.renderHook;
|
||||||
|
|
||||||
|
if (game.pf1.historyTab.observers instanceof Map) {
|
||||||
|
for (const observer of game.pf1.historyTab.observers.values()) {
|
||||||
|
observer?.disconnect();
|
||||||
|
}
|
||||||
|
delete game.pf1.historyTab.observers;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.notifications.info("History tab hooks disabled. Close and reopen actor sheets to remove the tab.");
|
||||||
234
src/macro_enable-history-dialog.js
Normal file
234
src/macro_enable-history-dialog.js
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* Adds small log buttons to PF1 actor sheets (HP, XP, Currency headers).
|
||||||
|
* Buttons open a modal showing HP / XP / Currency logs stored in world flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const HISTORY_BUTTON_CLASS = "pf1-history-btn";
|
||||||
|
const BUTTON_TITLE = "Open Log";
|
||||||
|
const BUTTON_ICON = '<i class="fas fa-clock-rotate-left"></i>';
|
||||||
|
const BUTTON_STYLE =
|
||||||
|
"position:absolute;right:6px;top:4px;border:none;background:transparent;padding:0;width:18px;height:18px;display:flex;align-items:center;justify-content:center;color:inherit;cursor:pointer;";
|
||||||
|
|
||||||
|
const LOG_TARGETS = [
|
||||||
|
{ key: "hp", tab: "hp", finder: findHpContainer },
|
||||||
|
{ key: "xp", tab: "xp", finder: findXpContainer },
|
||||||
|
{ key: "currency", tab: "currency", finder: findCurrencyContainer },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SHEET_EVENTS = [
|
||||||
|
"renderActorSheetPFCharacter",
|
||||||
|
"renderActorSheetPFNPC",
|
||||||
|
"renderActorSheetPFNPCLoot",
|
||||||
|
"renderActorSheetPFNPCLite",
|
||||||
|
"renderActorSheetPFTrap",
|
||||||
|
"renderActorSheetPFVehicle",
|
||||||
|
"renderActorSheetPFHaunt",
|
||||||
|
"renderActorSheetPFBasic",
|
||||||
|
];
|
||||||
|
|
||||||
|
game.pf1 ??= {};
|
||||||
|
game.pf1.historyDialog ??= { observers: new Map(), renderHooks: [] };
|
||||||
|
|
||||||
|
if (game.pf1.historyDialog.renderHooks.length) {
|
||||||
|
return ui.notifications.info("Log buttons already active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
game.pf1.historyDialog.renderHooks = SHEET_EVENTS.map((event) => {
|
||||||
|
const id = Hooks.on(event, (sheet) => {
|
||||||
|
const delays = [0, 100, 250];
|
||||||
|
delays.forEach((delay) =>
|
||||||
|
setTimeout(() => {
|
||||||
|
attachButtons(sheet);
|
||||||
|
}, delay)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return { event, id };
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.notifications.info("PF1 log buttons enabled. Check the HP/XP/Currency headers.");
|
||||||
|
|
||||||
|
function attachButtons(sheet) {
|
||||||
|
for (const targetCfg of LOG_TARGETS) {
|
||||||
|
const container = targetCfg.finder(sheet.element);
|
||||||
|
if (!container?.length) continue;
|
||||||
|
addButton(container, sheet.actor, targetCfg.tab, `${sheet.id}-${targetCfg.key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addButton(container, actor, tab, observerKey) {
|
||||||
|
if (!container.length) return;
|
||||||
|
if (container.find(`.${HISTORY_BUTTON_CLASS}[data-log-tab="${tab}"]`).length) return;
|
||||||
|
|
||||||
|
if (container.css("position") === "static") container.css("position", "relative");
|
||||||
|
|
||||||
|
const button = $(`
|
||||||
|
<button type="button" class="${HISTORY_BUTTON_CLASS}" data-log-tab="${tab}"
|
||||||
|
title="${BUTTON_TITLE}" style="${BUTTON_STYLE}">
|
||||||
|
${BUTTON_ICON}
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
button.on("click", () => openHistoryDialog(actor, tab));
|
||||||
|
container.append(button);
|
||||||
|
|
||||||
|
observeContainer(container, actor, tab, observerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function observeContainer(container, actor, tab, key) {
|
||||||
|
if (game.pf1.historyDialog.observers.has(key)) return;
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => addButton(container, actor, tab, key));
|
||||||
|
observer.observe(container[0], { childList: true });
|
||||||
|
game.pf1.historyDialog.observers.set(key, observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findHpContainer(root) {
|
||||||
|
const header = root
|
||||||
|
.find("h3, label")
|
||||||
|
.filter((_, el) => el.textContent.trim() === "Hit Points")
|
||||||
|
.first();
|
||||||
|
if (!header.length) return null;
|
||||||
|
return header.parent().length ? header.parent() : header.closest(".attribute.hitpoints").first();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findXpContainer(root) {
|
||||||
|
const box = root.find(".info-box.experience").first();
|
||||||
|
if (box.length) return box;
|
||||||
|
const header = root
|
||||||
|
.find("h5, label")
|
||||||
|
.filter((_, el) => el.textContent.trim().toLowerCase() === "experience")
|
||||||
|
.first();
|
||||||
|
return header.length ? header.parent() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCurrencyContainer(root) {
|
||||||
|
const summaryHeader = root
|
||||||
|
.find("h3, label")
|
||||||
|
.filter((_, el) => el.textContent.trim().toLowerCase() === "currency")
|
||||||
|
.first();
|
||||||
|
if (summaryHeader.length) return summaryHeader.parent();
|
||||||
|
return root.find(".currency").first();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openHistoryDialog(actor, initialTab = "hp") {
|
||||||
|
if (!actor) return;
|
||||||
|
const content = buildHistoryContent(actor, initialTab);
|
||||||
|
new Dialog(
|
||||||
|
{
|
||||||
|
title: `${actor.name}: Log`,
|
||||||
|
content,
|
||||||
|
buttons: { close: { label: "Close" } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
width: 720,
|
||||||
|
classes: ["pf1-history-dialog"],
|
||||||
|
}
|
||||||
|
).render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildHistoryContent(actor, initialTab = "hp") {
|
||||||
|
const configs = [
|
||||||
|
{
|
||||||
|
id: "hp",
|
||||||
|
label: "HP",
|
||||||
|
flag: "pf1HpHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "HP", render: (e) => e.hp },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
{ label: "Source", render: (e) => e.source ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "xp",
|
||||||
|
label: "XP",
|
||||||
|
flag: "pf1XpHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "XP", render: (e) => e.value },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "currency",
|
||||||
|
label: "Currency",
|
||||||
|
flag: "pf1CurrencyHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "Totals", render: (e) => e.value },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tabs = configs
|
||||||
|
.map(
|
||||||
|
(cfg) =>
|
||||||
|
`<a class="history-tab ${cfg.id === initialTab ? "active" : ""}" data-history-tab="${cfg.id}">${cfg.label}</a>`
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
const panels = configs
|
||||||
|
.map((cfg) => {
|
||||||
|
const entries = actor.getFlag("world", cfg.flag) ?? [];
|
||||||
|
const table = renderHistoryTable(entries, cfg.columns, cfg.id);
|
||||||
|
const display = cfg.id === initialTab ? "block" : "none";
|
||||||
|
return `<section class="history-panel" data-history-panel="${cfg.id}" style="display:${display}">${table}</section>`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section class="history-dialog-root" data-history-root="${actor.id}">
|
||||||
|
<style>
|
||||||
|
.history-dialog-tabs { display:flex; gap:6px; margin-bottom:8px; }
|
||||||
|
.history-tab { flex:1; text-align:center; padding:4px 0; border:1px solid #b5b3a4; border-radius:3px; cursor:pointer; background:#ddd; }
|
||||||
|
.history-tab.active { background:#fff; font-weight:bold; }
|
||||||
|
</style>
|
||||||
|
<nav class="history-dialog-tabs">${tabs}</nav>
|
||||||
|
<div class="history-dialog-panels">${panels}</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function(){
|
||||||
|
const root = document.currentScript.closest('[data-history-root="${actor.id}"]');
|
||||||
|
if (!root) return;
|
||||||
|
const buttons = Array.from(root.querySelectorAll('.history-tab'));
|
||||||
|
const panels = Array.from(root.querySelectorAll('.history-panel'));
|
||||||
|
function activate(target) {
|
||||||
|
buttons.forEach((btn) => btn.classList.toggle('active', btn.dataset.historyTab === target));
|
||||||
|
panels.forEach((panel) => (panel.style.display = panel.dataset.historyPanel === target ? 'block' : 'none'));
|
||||||
|
}
|
||||||
|
buttons.forEach((btn) => btn.addEventListener('click', () => activate(btn.dataset.historyTab)));
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</section>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHistoryTable(entries, columns, id) {
|
||||||
|
if (!entries.length) {
|
||||||
|
return `<p class="history-empty">No ${id.toUpperCase()} history recorded.</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = entries
|
||||||
|
.map(
|
||||||
|
(entry) => `
|
||||||
|
<tr>
|
||||||
|
${columns.map((col) => `<td>${col.render(entry) ?? ""}</td>`).join("")}
|
||||||
|
</tr>`
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<table class="history-table history-${id}">
|
||||||
|
<thead>
|
||||||
|
<tr>${columns.map((col) => `<th>${col.label}</th>`).join("")}</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${rows}
|
||||||
|
</tbody>
|
||||||
|
</table>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(ts) {
|
||||||
|
return ts ? new Date(ts).toLocaleString() : "";
|
||||||
|
}
|
||||||
226
src/macro_enable-history-tab.js
Normal file
226
src/macro_enable-history-tab.js
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/**
|
||||||
|
* Add a History tab to PF1 actor sheets with HP/XP/Currency subtabs.
|
||||||
|
* Requires the corresponding flag-tracking macros to populate data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TAB_ID = "pf1-history";
|
||||||
|
const TAB_LABEL = "History";
|
||||||
|
|
||||||
|
if (!game.system.id.includes("pf1")) {
|
||||||
|
ui.notifications.warn("This macro is intended for the PF1 system.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game.pf1 ??= {};
|
||||||
|
game.pf1.historyTab ??= {};
|
||||||
|
|
||||||
|
const PF1_SHEET_HOOKS = [
|
||||||
|
"renderActorSheetPFCharacter",
|
||||||
|
"renderActorSheetPFNPC",
|
||||||
|
"renderActorSheetPFNPCLoot",
|
||||||
|
"renderActorSheetPFNPCLite",
|
||||||
|
"renderActorSheetPFTrap",
|
||||||
|
"renderActorSheetPFVehicle",
|
||||||
|
"renderActorSheetPFHaunt",
|
||||||
|
"renderActorSheetPFBasic",
|
||||||
|
];
|
||||||
|
|
||||||
|
game.pf1.historyTab.renderHooks ??= [];
|
||||||
|
if (game.pf1.historyTab.renderHooks.length) {
|
||||||
|
return ui.notifications.info("History tab hooks already active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const hook of PF1_SHEET_HOOKS) {
|
||||||
|
const id = Hooks.on(hook, (sheet, html) => {
|
||||||
|
const delays = [0, 50, 200];
|
||||||
|
delays.forEach((delay) =>
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
injectHistoryTab(sheet);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("PF1 History Tab | Failed to render history tab", err);
|
||||||
|
}
|
||||||
|
}, delay)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
game.pf1.historyTab.renderHooks.push({ event: hook, id });
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.notifications.info("PF1 History tab enabled. Reopen actor sheets to see the new tab.");
|
||||||
|
|
||||||
|
function injectHistoryTab(sheet) {
|
||||||
|
if (!sheet?.element?.length) return;
|
||||||
|
console.log("PF1 History Tab | Injecting for", sheet.actor?.name, sheet.id);
|
||||||
|
const actor = sheet.actor;
|
||||||
|
const nav = sheet.element.find('.sheet-navigation.tabs[data-group="primary"]').first();
|
||||||
|
const body = sheet.element.find(".sheet-body").first();
|
||||||
|
if (!nav.length || !body.length) return;
|
||||||
|
|
||||||
|
ensureHistoryTab(nav);
|
||||||
|
|
||||||
|
const existing = body.find(`.tab[data-tab="${TAB_ID}"]`).first();
|
||||||
|
const content = buildHistoryContent(actor);
|
||||||
|
if (existing.length) existing.html(content);
|
||||||
|
else body.append(`<div class="tab history-tab" data-tab="${TAB_ID}" data-group="primary">${content}</div>`);
|
||||||
|
|
||||||
|
sheet._tabs?.forEach((tabs) => tabs.bind(sheet.element[0]));
|
||||||
|
|
||||||
|
setupObserver(sheet, nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureHistoryTab(nav) {
|
||||||
|
if (nav.find(`[data-tab="${TAB_ID}"]`).length) return;
|
||||||
|
nav.append(`<a class="item" data-group="primary" data-tab="${TAB_ID}">${TAB_LABEL}</a>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupObserver(sheet, nav) {
|
||||||
|
game.pf1.historyTab.observers ??= new Map();
|
||||||
|
const key = sheet.id;
|
||||||
|
if (game.pf1.historyTab.observers.has(key)) return;
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const currentNav = sheet.element.find('.sheet-navigation.tabs[data-group="primary"]').first();
|
||||||
|
if (!currentNav.length) return;
|
||||||
|
ensureHistoryTab(currentNav);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(sheet.element[0], { childList: true, subtree: true });
|
||||||
|
game.pf1.historyTab.observers.set(key, observer);
|
||||||
|
|
||||||
|
const closeHandler = () => {
|
||||||
|
observer.disconnect();
|
||||||
|
game.pf1.historyTab.observers.delete(key);
|
||||||
|
sheet.element.off("remove", closeHandler);
|
||||||
|
};
|
||||||
|
sheet.element.on("remove", closeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildHistoryContent(actor) {
|
||||||
|
const configs = [
|
||||||
|
{
|
||||||
|
id: "hp",
|
||||||
|
label: "HP",
|
||||||
|
flag: "pf1HpHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "HP", render: (e) => e.hp },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
{ label: "Source", render: (e) => e.source ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "xp",
|
||||||
|
label: "XP",
|
||||||
|
flag: "pf1XpHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "XP", render: (e) => e.value },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "currency",
|
||||||
|
label: "Currency",
|
||||||
|
flag: "pf1CurrencyHistory",
|
||||||
|
columns: [
|
||||||
|
{ label: "Timestamp", render: (e) => formatDate(e.timestamp) },
|
||||||
|
{ label: "Totals", render: (e) => e.value },
|
||||||
|
{ label: "Δ", render: (e) => e.diff },
|
||||||
|
{ label: "User", render: (e) => e.user ?? "" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const nav = configs
|
||||||
|
.map((cfg, idx) => `<a class="history-subtab ${idx === 0 ? "active" : ""}" data-history-subtab="${cfg.id}">${cfg.label}</a>`)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
const panels = configs
|
||||||
|
.map((cfg, idx) => {
|
||||||
|
const entries = actor.getFlag("world", cfg.flag) ?? [];
|
||||||
|
const table = renderHistoryTable(entries, cfg.columns, cfg.id);
|
||||||
|
return `<section class="history-panel" data-history-panel="${cfg.id}" style="display:${idx === 0 ? "block" : "none"}">${table}</section>`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section class="pf1-history-root" data-history-block="history-tab">
|
||||||
|
<nav class="history-subnav">
|
||||||
|
${nav}
|
||||||
|
</nav>
|
||||||
|
<div class="history-panels">
|
||||||
|
${panels}
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function(){
|
||||||
|
const root = document.currentScript.closest('[data-history-block="history-tab"]');
|
||||||
|
if (!root) return;
|
||||||
|
const tabs = Array.from(root.querySelectorAll('[data-history-subtab]'));
|
||||||
|
const panels = Array.from(root.querySelectorAll('[data-history-panel]'));
|
||||||
|
tabs.forEach((btn) => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const target = btn.dataset.historySubtab;
|
||||||
|
tabs.forEach((n) => n.classList.toggle('active', n === btn));
|
||||||
|
panels.forEach((panel) => (panel.style.display = panel.dataset.historyPanel === target ? 'block' : 'none'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHistoryTable(entries, columns, id) {
|
||||||
|
if (!entries.length) return `<p class="history-empty">No ${id.toUpperCase()} history recorded.</p>`;
|
||||||
|
|
||||||
|
const rows = entries
|
||||||
|
.map(
|
||||||
|
(entry) => `
|
||||||
|
<tr data-ts="${entry.timestamp}">
|
||||||
|
${columns.map((col) => `<td>${col.render(entry) ?? ""}</td>`).join("")}
|
||||||
|
</tr>`
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="history-filters">
|
||||||
|
<label>From <input type="date" data-filter="from-${id}"></label>
|
||||||
|
<label>To <input type="date" data-filter="to-${id}"></label>
|
||||||
|
</div>
|
||||||
|
<table class="history-table history-${id}">
|
||||||
|
<thead>
|
||||||
|
<tr>${columns.map((col) => `<th>${col.label}</th>`).join("")}</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${rows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function(){
|
||||||
|
const root = document.currentScript.closest('.history-panel[data-history-panel="${id}"]');
|
||||||
|
if (!root) return;
|
||||||
|
const rows = Array.from(root.querySelectorAll("tbody tr"));
|
||||||
|
const fromInput = root.querySelector('input[data-filter="from-${id}"]');
|
||||||
|
const toInput = root.querySelector('input[data-filter="to-${id}"]');
|
||||||
|
function applyFilters(){
|
||||||
|
const from = fromInput?.value ? Date.parse(fromInput.value) : null;
|
||||||
|
const to = toInput?.value ? Date.parse(toInput.value) + 86399999 : null;
|
||||||
|
rows.forEach((row) => {
|
||||||
|
const ts = Number(row.dataset.ts);
|
||||||
|
const visible = (!from || ts >= from) && (!to || ts <= to);
|
||||||
|
row.style.display = visible ? "" : "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fromInput?.addEventListener("input", applyFilters);
|
||||||
|
toInput?.addEventListener("input", applyFilters);
|
||||||
|
applyFilters();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(ts) {
|
||||||
|
return new Date(ts).toLocaleString();
|
||||||
|
}
|
||||||
27
src/macro_stop-currency-history-tab.js
Normal file
27
src/macro_stop-currency-history-tab.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Stop currency flag history tracking (tab-based version).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = game.pf1?.currencyHistoryFlags;
|
||||||
|
if (!state) return ui.notifications.info("Currency flag history tracking is not active.");
|
||||||
|
|
||||||
|
const logKey = `pf1-currency-history-flags-${ACTOR_ID}`;
|
||||||
|
const handler = state.hooks?.[logKey];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return ui.notifications.info(`No currency flag history hook found for ${TARGET_ACTOR?.name ?? ACTOR_ID}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.off("updateActor", handler);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
if (state.current) delete state.current[ACTOR_ID];
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`Currency flag history tracking disabled for ${actorName}.`);
|
||||||
28
src/macro_stop-hp-history-tab.js
Normal file
28
src/macro_stop-hp-history-tab.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Stop HP flag history tracking (tab-based version).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = game.pf1?.hpHistoryFlags;
|
||||||
|
if (!state) return ui.notifications.info("HP flag history tracking is not active.");
|
||||||
|
|
||||||
|
const logKey = `pf1-hp-history-flags-${ACTOR_ID}`;
|
||||||
|
const handler = state.hooks?.[logKey];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return ui.notifications.info(`No HP flag history hook found for ${TARGET_ACTOR?.name ?? ACTOR_ID}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.off("updateActor", handler);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
state.sources?.delete(ACTOR_ID);
|
||||||
|
if (state.current) delete state.current[ACTOR_ID];
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`HP flag history tracking disabled for ${actorName}.`);
|
||||||
27
src/macro_stop-xp-history-tab.js
Normal file
27
src/macro_stop-xp-history-tab.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Stop XP flag history tracking (tab-based version).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TARGET_ACTOR = game.actors.getName("Zeratal") ?? null;
|
||||||
|
const ACTOR_ID = TARGET_ACTOR?.id ?? "put-actor-id-here";
|
||||||
|
|
||||||
|
if (!ACTOR_ID || ACTOR_ID === "put-actor-id-here") {
|
||||||
|
return ui.notifications.warn("Set ACTOR_ID before running the macro.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = game.pf1?.xpHistoryFlags;
|
||||||
|
if (!state) return ui.notifications.info("XP flag history tracking is not active.");
|
||||||
|
|
||||||
|
const logKey = `pf1-xp-history-flags-${ACTOR_ID}`;
|
||||||
|
const handler = state.hooks?.[logKey];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return ui.notifications.info(`No XP flag history hook found for ${TARGET_ACTOR?.name ?? ACTOR_ID}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.off("updateActor", handler);
|
||||||
|
delete state.hooks[logKey];
|
||||||
|
if (state.current) delete state.current[ACTOR_ID];
|
||||||
|
|
||||||
|
const actorName = game.actors.get(ACTOR_ID)?.name ?? TARGET_ACTOR?.name ?? ACTOR_ID;
|
||||||
|
ui.notifications.info(`XP flag history tracking disabled for ${actorName}.`);
|
||||||
46
src/modules/PFLedger/css/ledger.css
Normal file
46
src/modules/PFLedger/css/ledger.css
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
.pfledger-table, .pfledger-td {
|
||||||
|
border: 1px solid #000000;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 98%;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: oblique;
|
||||||
|
font-family: serif;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pfledger-th{
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
.pfledger-desc-box
|
||||||
|
{
|
||||||
|
min-width: 457px;
|
||||||
|
content: "\f58e";
|
||||||
|
font-weight: 900;
|
||||||
|
font-family: "Font Awesome 5 Free";
|
||||||
|
font-size: 12px;
|
||||||
|
font-variant: small-caps;
|
||||||
|
color: #4b4a44;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pfledger-button
|
||||||
|
{
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
margin: 0, 8px;
|
||||||
|
content: "\f58e";
|
||||||
|
font-weight: 900;
|
||||||
|
font-family: "Font Awesome 5 Free";
|
||||||
|
}
|
||||||
|
|
||||||
|
.pfledger-spacer
|
||||||
|
{
|
||||||
|
margin: 0 8px;
|
||||||
|
flex: 0 0 20px;
|
||||||
|
}
|
||||||
36
src/modules/PFLedger/module.json
Normal file
36
src/modules/PFLedger/module.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"title": "Pathfinder Ledger",
|
||||||
|
"description": "A simple module which records money in and out.",
|
||||||
|
"version": "1.0.7",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Lee 'Almighty_gir' Devonald",
|
||||||
|
"url": "https://github.com/Almightygir/PFLedger",
|
||||||
|
"discord": "Almighty_gir#6379",
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"scripts/ledger.js"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"css/ledger.css"
|
||||||
|
],
|
||||||
|
"url": "https://github.com/Almightygir/PFLedger",
|
||||||
|
"manifest": "https://github.com/Almightygir/PFLedger/releases/latest/download/module.json",
|
||||||
|
"download": "https://github.com/Almightygir/PFLedger/releases/download/v1.0.7/module.zip",
|
||||||
|
"id": "PFLedger",
|
||||||
|
"relationships": {
|
||||||
|
"systems": [
|
||||||
|
{
|
||||||
|
"id": "pf1",
|
||||||
|
"type": "system",
|
||||||
|
"compatibility": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compatibility": {
|
||||||
|
"minimum": "0.7.9",
|
||||||
|
"verified": "9"
|
||||||
|
}
|
||||||
|
}
|
||||||
326
src/modules/PFLedger/scripts/ledger.js
Normal file
326
src/modules/PFLedger/scripts/ledger.js
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
console.log('Ledger ! Im running!');
|
||||||
|
|
||||||
|
//Main class
|
||||||
|
class Ledger
|
||||||
|
{
|
||||||
|
static ID = 'PFLedger';
|
||||||
|
static FLAGS = { LEDGERS: 'ledgers' }
|
||||||
|
|
||||||
|
static TEMPLATES = { LEDGERLIST: `modules/${this.ID}/templates/ledger.hbs` }
|
||||||
|
|
||||||
|
static log(force, ...args)
|
||||||
|
{
|
||||||
|
const shouldLog = force || game.modules.get('_dev-mode')?.api?.getPackageDebugValue(this.ID);
|
||||||
|
|
||||||
|
if (shouldLog)
|
||||||
|
{
|
||||||
|
console.log(this.ID, '|', ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//data class
|
||||||
|
class LedgerData
|
||||||
|
{
|
||||||
|
//get all ledgers in the game
|
||||||
|
static get allLegers() {}
|
||||||
|
|
||||||
|
//get ledger for a giver nuser
|
||||||
|
static getLedgerForActor(actor)
|
||||||
|
{
|
||||||
|
return actor.getFlag(Ledger.ID, Ledger.FLAGS.LEDGERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
//creates a new entry for the ledger
|
||||||
|
static addLedgerEntry(actor, userId, inCurrency, inAltCurrency, inCurrencyDiff, inAltCurrencyDiff, inLedgerLog)
|
||||||
|
{
|
||||||
|
//generate new random id for this ledger entry and populate the userID
|
||||||
|
const actorId = actor.id;
|
||||||
|
const newLedgerEntry =
|
||||||
|
{
|
||||||
|
Log: inLedgerLog,
|
||||||
|
currency: inCurrency,
|
||||||
|
altCurrency: inAltCurrency,
|
||||||
|
currencyDiff: inCurrencyDiff,
|
||||||
|
altCurrencyDiff: inAltCurrencyDiff,
|
||||||
|
Character: actor.name,
|
||||||
|
UserName: game.users.get(userId).name,
|
||||||
|
id: foundry.utils.randomID(16),
|
||||||
|
ActorId: actorId,
|
||||||
|
}
|
||||||
|
const newEntries = { [newLedgerEntry.id]: newLedgerEntry }
|
||||||
|
|
||||||
|
return game.actors.get(actorId)?.setFlag(Ledger.ID, Ledger.FLAGS.LEDGERS, newEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getActorLedgerLastEntry(actor)
|
||||||
|
{
|
||||||
|
const ledgerEntries = this.getLedgerForActor(actor);
|
||||||
|
if(ledgerEntries)
|
||||||
|
{
|
||||||
|
let actorEntries = new Array();
|
||||||
|
for(const ledgerEntry of Object.values(ledgerEntries))
|
||||||
|
{
|
||||||
|
if(ledgerEntry)
|
||||||
|
{
|
||||||
|
if(ledgerEntry.ActorId === actor.id)
|
||||||
|
{
|
||||||
|
actorEntries.push(ledgerEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(actorEntries.length > 0)
|
||||||
|
{
|
||||||
|
return actorEntries[actorEntries.length-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//useful stuff
|
||||||
|
class CashConverter
|
||||||
|
{
|
||||||
|
static convertCurrencyToCP(currency)
|
||||||
|
{
|
||||||
|
let CP = currency.cp;
|
||||||
|
CP += currency.sp * 10;
|
||||||
|
CP += currency.gp * 100;
|
||||||
|
CP += currency.pp * 1000;
|
||||||
|
|
||||||
|
return CP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertToCP(inCP, inSP, inGP, inPP)
|
||||||
|
{
|
||||||
|
let CP = inCP;
|
||||||
|
CP += inSP * 10;
|
||||||
|
CP += inGP * 100;
|
||||||
|
CP += inPP * 1000;
|
||||||
|
|
||||||
|
return CP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static currencyCheck(inCurrencyA, inCurrencyB)
|
||||||
|
{
|
||||||
|
return inCurrencyA.cp == inCurrencyB.cp &&
|
||||||
|
inCurrencyA.sp == inCurrencyB.sp &&
|
||||||
|
inCurrencyA.gp == inCurrencyB.gp &&
|
||||||
|
inCurrencyA.pp == inCurrencyB.pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCurrencyDelta(inCurrencyA, inCurrencyB)
|
||||||
|
{
|
||||||
|
const currency =
|
||||||
|
{
|
||||||
|
cp : inCurrencyA.cp - inCurrencyB.cp,
|
||||||
|
sp : inCurrencyA.sp - inCurrencyB.sp,
|
||||||
|
gp : inCurrencyA.gp - inCurrencyB.gp,
|
||||||
|
pp : inCurrencyA.pp - inCurrencyB.pp,
|
||||||
|
}
|
||||||
|
|
||||||
|
currency.cp = currency.cp ? currency.cp : 0;
|
||||||
|
currency.sp = currency.sp ? currency.sp : 0;
|
||||||
|
currency.gp = currency.gp ? currency.gp : 0;
|
||||||
|
currency.pp = currency.pp ? currency.pp : 0;
|
||||||
|
|
||||||
|
return currency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//the ledger form
|
||||||
|
class LedgerForm extends FormApplication
|
||||||
|
{
|
||||||
|
static get defaultOptions()
|
||||||
|
{
|
||||||
|
const defaults = super.defaultOptions;
|
||||||
|
|
||||||
|
const overrides =
|
||||||
|
{
|
||||||
|
height: '700',
|
||||||
|
width: '650',
|
||||||
|
id: 'ledger',
|
||||||
|
template: Ledger.TEMPLATES.LEDGERLIST,
|
||||||
|
title: 'Ledger',
|
||||||
|
currencies: ['cp', 'sp', 'gp', 'pp'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
|
||||||
|
|
||||||
|
return mergedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getData(options)
|
||||||
|
{
|
||||||
|
let outLedgers = {ledgers: LedgerData.getLedgerForActor(this.object)};
|
||||||
|
return outLedgers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLedgerEntry_Ext(actor, description)
|
||||||
|
{
|
||||||
|
//if for any reason actor is null, return early.
|
||||||
|
if(actor === null)
|
||||||
|
{
|
||||||
|
console.log('Ledger ! Actor is null, exiting early');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userId = game.user.id;
|
||||||
|
const altCurrency = actor.data.data.altCurrency;
|
||||||
|
const currency = actor.data.data.currency;
|
||||||
|
|
||||||
|
const lastEntry = LedgerData.getActorLedgerLastEntry(actor, userId);
|
||||||
|
//if the last entry is null, it doesn't exist, so we should make a new entry.
|
||||||
|
if(lastEntry === null)
|
||||||
|
{
|
||||||
|
console.log('Ledger ! No last entry found, creating new entry');
|
||||||
|
LedgerData.addLedgerEntry(actor, userId, currency, altCurrency, currency, altCurrency, "Initial entry.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//check if there's a delta, if there is we need to create an entry.
|
||||||
|
if(!CashConverter.currencyCheck(altCurrency, lastEntry.altCurrency) || !CashConverter.currencyCheck(currency, lastEntry.currency))
|
||||||
|
{
|
||||||
|
console.log('Ledger ! Changes detected, adding a new entry!');
|
||||||
|
const currencyDiff = CashConverter.getCurrencyDelta(currency, lastEntry.currency);
|
||||||
|
const altCurrencyDiff = CashConverter.getCurrencyDelta(altCurrency, lastEntry.altCurrency);
|
||||||
|
LedgerData.addLedgerEntry(actor, userId, currency, altCurrency, currencyDiff, altCurrencyDiff, description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActorLedger_Ext(actor)
|
||||||
|
{
|
||||||
|
let ledgerForm = new LedgerForm(actor).render(true, {actor});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLedgerButtons(sheet, jq, data)
|
||||||
|
{
|
||||||
|
const actor = data.actor;
|
||||||
|
if (!actor || !actor.isOwner)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = jq[0];
|
||||||
|
const tab = html.querySelector('.tab.inventory');
|
||||||
|
if (!tab)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currencyTab = tab.querySelector('.inventory-filters');
|
||||||
|
if (!currencyTab)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTooltip = 'Add ledger entry';
|
||||||
|
const openLedgerTooltip = 'Opens the ledger';
|
||||||
|
const descBoxTooltip = 'Add ledger entry';
|
||||||
|
|
||||||
|
const newRow = document.createElement("flexrow");
|
||||||
|
newRow.classList.add("pfledger-spacer");
|
||||||
|
|
||||||
|
const descriptionBox = document.createElement("input");
|
||||||
|
descriptionBox.classList.add("pfledger-desc-box");
|
||||||
|
descriptionBox.setAttribute("value", "...");
|
||||||
|
descriptionBox.title = descBoxTooltip;
|
||||||
|
currencyTab.append(descriptionBox);
|
||||||
|
|
||||||
|
const updateButton = document.createElement("button");
|
||||||
|
updateButton.classList.add("pfledger-button");
|
||||||
|
updateButton.textContent = "Update Ledger";
|
||||||
|
updateButton.title = updateTooltip;
|
||||||
|
updateButton.addEventListener("click", event => {
|
||||||
|
addLedgerEntry_Ext(actor, descriptionBox.value)
|
||||||
|
});
|
||||||
|
currencyTab.append(updateButton);
|
||||||
|
|
||||||
|
const openLedgerButton = document.createElement("button");
|
||||||
|
openLedgerButton.classList.add("pfledger-button");
|
||||||
|
openLedgerButton.textContent = "Open Ledger...";
|
||||||
|
openLedgerButton.title = openLedgerTooltip;
|
||||||
|
openLedgerButton.addEventListener("click", event => {
|
||||||
|
getActorLedger_Ext(actor)
|
||||||
|
});
|
||||||
|
currencyTab.append(openLedgerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.on('renderActorSheetPF', addLedgerButtons);
|
||||||
|
|
||||||
|
|
||||||
|
//GM ledger forms.
|
||||||
|
class ledgerFormGM extends FormApplication
|
||||||
|
{
|
||||||
|
static get defaultOptions()
|
||||||
|
{
|
||||||
|
const defaults = super.defaultOptions;
|
||||||
|
|
||||||
|
const overrides =
|
||||||
|
{
|
||||||
|
height: '700',
|
||||||
|
width: '650',
|
||||||
|
id: 'ledger',
|
||||||
|
template: Ledger.TEMPLATES.LEDGERLIST,
|
||||||
|
title: 'Ledger',
|
||||||
|
currencies: ['cp', 'sp', 'gp', 'pp'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
|
||||||
|
|
||||||
|
return mergedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getData(options)
|
||||||
|
{
|
||||||
|
let allLedgers;
|
||||||
|
game.actors.forEach(actor =>{
|
||||||
|
const currentLedgers = LedgerData.getLedgerForActor(actor);
|
||||||
|
if(currentLedgers)
|
||||||
|
{
|
||||||
|
if(!allLedgers)
|
||||||
|
{
|
||||||
|
allLedgers = currentLedgers;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allLedgers = {...allLedgers, ...currentLedgers};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {ledgers: allLedgers};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGMLedger_Ext()
|
||||||
|
{
|
||||||
|
let gmLedgerForm = new ledgerFormGM().render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GMLedgerButton(sheet)
|
||||||
|
{
|
||||||
|
if(!game.user.isGM)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openLedgerTooltip = 'Opens the combined player ledgers';
|
||||||
|
const ledgerButtonContents = 'Open Ledgers...';
|
||||||
|
|
||||||
|
const pfDetailsTab = sheet.element[0].querySelector('#pf1-details');
|
||||||
|
if(!pfDetailsTab || pfDetailsTab.querySelector('#PathfinderGMLedgerButton'))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const openGMLedgerButton = document.createElement("button");
|
||||||
|
openGMLedgerButton.textContent = ledgerButtonContents;
|
||||||
|
openGMLedgerButton.title = openLedgerTooltip;
|
||||||
|
openGMLedgerButton.id = "PathfinderGMLedgerButton";
|
||||||
|
openGMLedgerButton.addEventListener("click", event =>{
|
||||||
|
getGMLedger_Ext();
|
||||||
|
})
|
||||||
|
pfDetailsTab.append(openGMLedgerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.on('changeSidebarTab', GMLedgerButton);
|
||||||
57
src/modules/PFLedger/templates/ledger.hbs
Normal file
57
src/modules/PFLedger/templates/ledger.hbs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{{log 'ledgers' this}}
|
||||||
|
<div class = "pfledger">
|
||||||
|
<form>
|
||||||
|
<ul>
|
||||||
|
<table class = "pfledger-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class = "pfledger-th">Log</th>
|
||||||
|
<th class = "pfledger-th">PP</th>
|
||||||
|
<th class = "pfledger-th">GP</th>
|
||||||
|
<th class = "pfledger-th">SP</th>
|
||||||
|
<th class = "pfledger-th">CP</th>
|
||||||
|
<th class = "pfledger-th">WL:</th>
|
||||||
|
<th class = "pfledger-th">PP</th>
|
||||||
|
<th class = "pfledger-th">GP</th>
|
||||||
|
<th class = "pfledger-th">SP</th>
|
||||||
|
<th class = "pfledger-th">CP</th>
|
||||||
|
<th class = "pfledger-th">Character</th>
|
||||||
|
<th class = "pfledger-th">User</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each ledgers}}
|
||||||
|
<tr>
|
||||||
|
<td class = "pfledger-td">{{Log}}</td>
|
||||||
|
<td class = "pfledger-td">{{currencyDiff.pp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currencyDiff.gp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currencyDiff.sp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currencyDiff.cp}}</td>
|
||||||
|
<td class = "pfledger-td"></td>
|
||||||
|
<td class = "pfledger-td">{{altCurrencyDiff.pp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrencyDiff.gp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrencyDiff.sp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrencyDiff.cp}}</td>
|
||||||
|
<td class = "pfledger-td">{{Character}}</td>
|
||||||
|
<td class = "pfledger-td">{{UserName}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class = "pfledger-td">New Total:</td>
|
||||||
|
<td class = "pfledger-td">{{currency.pp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currency.gp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currency.sp}}</td>
|
||||||
|
<td class = "pfledger-td">{{currency.cp}}</td>
|
||||||
|
<td class = "pfledger-td"></td>
|
||||||
|
<td class = "pfledger-td">{{altCurrency.pp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrency.gp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrency.sp}}</td>
|
||||||
|
<td class = "pfledger-td">{{altCurrency.cp}}</td>
|
||||||
|
<td class = "pfledger-td"></td>
|
||||||
|
<td class = "pfledger-td"></td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
7
src/modules/_CodeMirror/.editorconfig
Normal file
7
src/modules/_CodeMirror/.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
8
src/modules/_CodeMirror/.gitattributes
vendored
Normal file
8
src/modules/_CodeMirror/.gitattributes
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
*.txt text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.svg text eol=lf
|
||||||
1
src/modules/_CodeMirror/.gitignore
vendored
Normal file
1
src/modules/_CodeMirror/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.zip
|
||||||
14
src/modules/_CodeMirror/.npmignore
Normal file
14
src/modules/_CodeMirror/.npmignore
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/node_modules
|
||||||
|
/demo
|
||||||
|
/doc
|
||||||
|
/test
|
||||||
|
/test*.html
|
||||||
|
/index.html
|
||||||
|
/mode/*/*test.js
|
||||||
|
/mode/*/*.html
|
||||||
|
/mode/index.html
|
||||||
|
.*
|
||||||
|
/bin/authors.sh
|
||||||
|
/bin/lint
|
||||||
|
/bin/release
|
||||||
|
/bin/upload-release.js
|
||||||
5
src/modules/_CodeMirror/.travis.yml
Normal file
5
src/modules/_CodeMirror/.travis.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- stable
|
||||||
|
sudo: false
|
||||||
|
cache: npm
|
||||||
901
src/modules/_CodeMirror/AUTHORS
Normal file
901
src/modules/_CodeMirror/AUTHORS
Normal file
@@ -0,0 +1,901 @@
|
|||||||
|
List of CodeMirror contributors. Updated before every release.
|
||||||
|
|
||||||
|
4oo4
|
||||||
|
4r2r
|
||||||
|
Aaron Brooks
|
||||||
|
Abdelouahab
|
||||||
|
Abdussalam Abdurrahman
|
||||||
|
Abe Fettig
|
||||||
|
Abhishek Gahlot
|
||||||
|
Adam Ahmed
|
||||||
|
Adam King
|
||||||
|
Adam Particka
|
||||||
|
adanlobato
|
||||||
|
Adán Lobato
|
||||||
|
Aditya Toshniwal
|
||||||
|
Adrian Aichner
|
||||||
|
Adrian Heine
|
||||||
|
Adrian Kunz
|
||||||
|
Adrien Bertrand
|
||||||
|
aeroson
|
||||||
|
Ahmad Amireh
|
||||||
|
Ahmad M. Zawawi
|
||||||
|
ahoward
|
||||||
|
Ajin Abraham
|
||||||
|
Akeksandr Motsjonov
|
||||||
|
Alasdair Smith
|
||||||
|
AlbertHilb
|
||||||
|
Alberto González Palomo
|
||||||
|
Alberto Pose
|
||||||
|
Albert Xing
|
||||||
|
Alexander Pavlov
|
||||||
|
Alexander Schepanovski
|
||||||
|
Alexander Shvets
|
||||||
|
Alexander Solovyov
|
||||||
|
Alexandre Bique
|
||||||
|
Alex Churchill
|
||||||
|
alexey-k
|
||||||
|
Alex Piggott
|
||||||
|
Aliaksei Chapyzhenka
|
||||||
|
Allen Sarkisyan
|
||||||
|
Ami Fischman
|
||||||
|
Amin Shali
|
||||||
|
Amin Ullah Khan
|
||||||
|
amshali@google.com
|
||||||
|
Amsul
|
||||||
|
amuntean
|
||||||
|
Amy
|
||||||
|
Ananya Sen
|
||||||
|
anaran
|
||||||
|
AndersMad
|
||||||
|
Anders Nawroth
|
||||||
|
Anderson Mesquita
|
||||||
|
Anders Wåglund
|
||||||
|
Andrea G
|
||||||
|
Andreas Reischuck
|
||||||
|
Andres Taylor
|
||||||
|
Andre von Houck
|
||||||
|
Andrew Cheng
|
||||||
|
Andrew Dassonville
|
||||||
|
Andrey Fedorov
|
||||||
|
Andrey Klyuchnikov
|
||||||
|
Andrey Lushnikov
|
||||||
|
Andrey Shchekin
|
||||||
|
Andy Joslin
|
||||||
|
Andy Kimball
|
||||||
|
Andy Li
|
||||||
|
Angelo
|
||||||
|
angelozerr
|
||||||
|
angelo.zerr@gmail.com
|
||||||
|
Ankit
|
||||||
|
Ankit Ahuja
|
||||||
|
Ansel Santosa
|
||||||
|
Anthony Dugois
|
||||||
|
anthonygego
|
||||||
|
Anthony Gégo
|
||||||
|
Anthony Grimes
|
||||||
|
Anton Kovalyov
|
||||||
|
antosarho
|
||||||
|
Apollo Zhu
|
||||||
|
AQNOUCH Mohammed
|
||||||
|
Aram Shatakhtsyan
|
||||||
|
areos
|
||||||
|
Arnab Bose
|
||||||
|
Arnoud Buzing
|
||||||
|
Arsène von Wyss
|
||||||
|
Arthur Müller
|
||||||
|
Arun Narasani
|
||||||
|
as3boyan
|
||||||
|
asolove
|
||||||
|
atelierbram
|
||||||
|
AtomicPages LLC
|
||||||
|
Atul Bhouraskar
|
||||||
|
Aurelian Oancea
|
||||||
|
Axel Lewenhaupt
|
||||||
|
Baptiste Augrain
|
||||||
|
Barret Rennie
|
||||||
|
Bartosz Dziewoński
|
||||||
|
Basarat Ali Syed
|
||||||
|
Bastian Müller
|
||||||
|
belhaj
|
||||||
|
Bem Jones-Bey
|
||||||
|
benbro
|
||||||
|
Benedikt Meurer
|
||||||
|
benhormann
|
||||||
|
Ben Hormann
|
||||||
|
Beni Cherniavsky-Paskin
|
||||||
|
Benjamin DeCoste
|
||||||
|
Benjamin Young
|
||||||
|
Ben Keen
|
||||||
|
Ben Miller
|
||||||
|
Ben Mosher
|
||||||
|
Bernhard Sirlinger
|
||||||
|
Bert Chang
|
||||||
|
Bharad
|
||||||
|
BigBlueHat
|
||||||
|
Billy Moon
|
||||||
|
Bin Ni
|
||||||
|
binny
|
||||||
|
Bjorn Hansen
|
||||||
|
B Krishna Chaitanya
|
||||||
|
Blaine G
|
||||||
|
blukat29
|
||||||
|
Bo
|
||||||
|
boomyjee
|
||||||
|
Bo Peng
|
||||||
|
borawjm
|
||||||
|
Boris K
|
||||||
|
Brad Metcalf
|
||||||
|
Brandon Frohs
|
||||||
|
Brandon Wamboldt
|
||||||
|
Bret Little
|
||||||
|
Brett Zamir
|
||||||
|
Brian Grinstead
|
||||||
|
BrianHung
|
||||||
|
Brian Sletten
|
||||||
|
brrd
|
||||||
|
Bruce Mitchener
|
||||||
|
Bruno Logerfo
|
||||||
|
Bryan Gin-ge Chen
|
||||||
|
Bryan Massoth
|
||||||
|
Caitlin Potter
|
||||||
|
Calin Barbat
|
||||||
|
callodacity
|
||||||
|
Camilo Roca
|
||||||
|
Casey Klebba
|
||||||
|
cBiscuit87
|
||||||
|
César González Íñiguez
|
||||||
|
Chad Jolly
|
||||||
|
Chandra Sekhar Pydi
|
||||||
|
Charles Skelton
|
||||||
|
Cheah Chu Yeow
|
||||||
|
Chhekur
|
||||||
|
Chris Colborne
|
||||||
|
Chris Coyier
|
||||||
|
Chris Ford
|
||||||
|
Chris Granger
|
||||||
|
Chris Houseknecht
|
||||||
|
Chris Lohfink
|
||||||
|
Chris Morgan
|
||||||
|
Chris Reeves
|
||||||
|
Chris Smith
|
||||||
|
Christian Gruen
|
||||||
|
Christian Oyarzun
|
||||||
|
Christian Petrov
|
||||||
|
christopherblaser
|
||||||
|
Christopher Brown
|
||||||
|
Christopher Kramer
|
||||||
|
Christopher Mitchell
|
||||||
|
Christopher Pfohl
|
||||||
|
Christopher Wallis
|
||||||
|
Chunliang Lyu
|
||||||
|
ciaranj
|
||||||
|
clone-it
|
||||||
|
clso
|
||||||
|
CodeAnimal
|
||||||
|
CodeBitt
|
||||||
|
coderaiser
|
||||||
|
Cole R Lawrence
|
||||||
|
ComFreek
|
||||||
|
Cristian Prieto
|
||||||
|
Curran Kelleher
|
||||||
|
Curtis Gagliardi
|
||||||
|
d8888
|
||||||
|
dagsta
|
||||||
|
daines
|
||||||
|
Dale Jung
|
||||||
|
Dan Bentley
|
||||||
|
Dan Heberden
|
||||||
|
Daniel, Dao Quang Minh
|
||||||
|
Daniele Di Sarli
|
||||||
|
Daniel Faust
|
||||||
|
Daniel Hanggi
|
||||||
|
Daniel Huigens
|
||||||
|
Daniel Kesler
|
||||||
|
Daniel KJ
|
||||||
|
Daniel Neel
|
||||||
|
Daniel Parnell
|
||||||
|
Daniel Thwaites
|
||||||
|
Danila Malyutin
|
||||||
|
Danny Yoo
|
||||||
|
darealshinji
|
||||||
|
Darius Roberts
|
||||||
|
databricks-david-lewis
|
||||||
|
Dave Brondsema
|
||||||
|
Dave MacLachlan
|
||||||
|
Dave Myers
|
||||||
|
David Barnett
|
||||||
|
David H. Bronke
|
||||||
|
David Mignot
|
||||||
|
David Pathakjee
|
||||||
|
David R. Myers
|
||||||
|
David Rodrigues
|
||||||
|
David Santana
|
||||||
|
David Vázquez
|
||||||
|
David Whittington
|
||||||
|
deebugger
|
||||||
|
Deep Thought
|
||||||
|
Denis Ovsienko
|
||||||
|
Devin Abbott
|
||||||
|
Devon Carew
|
||||||
|
Dick Choi
|
||||||
|
Diego Fernandez
|
||||||
|
dignifiedquire
|
||||||
|
Dimage Sapelkin
|
||||||
|
Dinindu D. Wanniarachchi
|
||||||
|
dmaclach
|
||||||
|
Dmitry Kiselyov
|
||||||
|
domagoj412
|
||||||
|
Dominator008
|
||||||
|
Domizio Demichelis
|
||||||
|
Doug Blank
|
||||||
|
Doug Wikle
|
||||||
|
Drew Bratcher
|
||||||
|
Drew Hintz
|
||||||
|
Drew Khoury
|
||||||
|
Drini Cami
|
||||||
|
Dror BG
|
||||||
|
Duncan Lilley
|
||||||
|
duralog
|
||||||
|
dwelle
|
||||||
|
Ealton
|
||||||
|
eborden
|
||||||
|
edoroshenko
|
||||||
|
edsharp
|
||||||
|
ekhaled
|
||||||
|
Elisée
|
||||||
|
elpnt
|
||||||
|
Emmanuel Schanzer
|
||||||
|
Enam Mijbah Noor
|
||||||
|
Eric Allam
|
||||||
|
Eric Bogard
|
||||||
|
Erik Demaine
|
||||||
|
Erik Welander
|
||||||
|
erosman
|
||||||
|
eustas
|
||||||
|
Evan Minsk
|
||||||
|
Fabien Dubosson
|
||||||
|
Fabien O'Carroll
|
||||||
|
Fabio Zendhi Nagao
|
||||||
|
Faiza Alsaied
|
||||||
|
Fauntleroy
|
||||||
|
fbuchinger
|
||||||
|
feizhang365
|
||||||
|
Felipe Lalanne
|
||||||
|
Felix Raab
|
||||||
|
ficristo
|
||||||
|
Filip Noetzel
|
||||||
|
Filip Stollár
|
||||||
|
Filype Pereira
|
||||||
|
finalfantasia
|
||||||
|
flack
|
||||||
|
Florian Felten
|
||||||
|
Fons van der Plas
|
||||||
|
Forbes Lindesay
|
||||||
|
ForbesLindesay
|
||||||
|
Ford_Lawnmower
|
||||||
|
Forrest Oliphant
|
||||||
|
Franco Catena
|
||||||
|
Frank Seifferth
|
||||||
|
Frank Wiegand
|
||||||
|
fraxx001
|
||||||
|
Fredrik Borg
|
||||||
|
FUJI Goro (gfx)
|
||||||
|
Gabriel Gheorghian
|
||||||
|
Gabriel Horner
|
||||||
|
Gabriel Nahmias
|
||||||
|
galambalazs
|
||||||
|
Gary Sheng
|
||||||
|
Gautam Mehta
|
||||||
|
Gavin Douglas
|
||||||
|
gekkoe
|
||||||
|
Geordie Hall
|
||||||
|
George Stephanis
|
||||||
|
geowarin
|
||||||
|
Gerard Braad
|
||||||
|
Gergely Hegykozi
|
||||||
|
Germain Chazot
|
||||||
|
Giovanni Calò
|
||||||
|
Glebov Boris
|
||||||
|
Glenn Jorde
|
||||||
|
Glenn Ruehle
|
||||||
|
goldsmcb
|
||||||
|
Golevka
|
||||||
|
Google LLC
|
||||||
|
Gordon Smith
|
||||||
|
Grant Skinner
|
||||||
|
greengiant
|
||||||
|
Gregory Koberger
|
||||||
|
Grzegorz Mazur
|
||||||
|
Guang Li
|
||||||
|
Guan Gui
|
||||||
|
Guillaume Massé
|
||||||
|
Guillaume Massé
|
||||||
|
guraga
|
||||||
|
Gustavo Rodrigues
|
||||||
|
Hakan Tunc
|
||||||
|
Hanno Fellmann
|
||||||
|
Hans Engel
|
||||||
|
Hanzhao Deng
|
||||||
|
Haoran Yu
|
||||||
|
Harald Schilly
|
||||||
|
Hardest
|
||||||
|
Harshvardhan Gupta
|
||||||
|
Hasan Delibaş
|
||||||
|
Hasan Karahan
|
||||||
|
Heanes
|
||||||
|
Hector Oswaldo Caballero
|
||||||
|
Hein Htat
|
||||||
|
Hélio
|
||||||
|
Hendrik Erz
|
||||||
|
Hendrik Wallbaum
|
||||||
|
Henrik Haugbølle
|
||||||
|
Herculano Campos
|
||||||
|
hidaiy
|
||||||
|
Hiroyuki Makino
|
||||||
|
hitsthings
|
||||||
|
Hocdoc
|
||||||
|
Howard
|
||||||
|
Howard Jing
|
||||||
|
Hugues Malphettes
|
||||||
|
Ian Beck
|
||||||
|
Ian Davies
|
||||||
|
Ian Dickinson
|
||||||
|
ianhi
|
||||||
|
Ian Rose
|
||||||
|
Ian Wehrman
|
||||||
|
Ian Wetherbee
|
||||||
|
Ice White
|
||||||
|
ICHIKAWA, Yuji
|
||||||
|
idleberg
|
||||||
|
Igor Petruk
|
||||||
|
ilvalle
|
||||||
|
Ilya Kharlamov
|
||||||
|
Ilya Zverev
|
||||||
|
Ingo Richter
|
||||||
|
Intervue
|
||||||
|
Irakli Gozalishvili
|
||||||
|
iteriani
|
||||||
|
Ivan Kurnosov
|
||||||
|
Ivoah
|
||||||
|
Jack Douglas
|
||||||
|
Jacob Lee
|
||||||
|
Jaimin
|
||||||
|
Jake Peyser
|
||||||
|
Jakob Miland
|
||||||
|
Jakub Vrana
|
||||||
|
Jakub Vrána
|
||||||
|
James Campos
|
||||||
|
James Cockshull
|
||||||
|
James Howard
|
||||||
|
James Thorne
|
||||||
|
Jamie Hill
|
||||||
|
Jamie Morris
|
||||||
|
Janice Leung
|
||||||
|
Jan Jongboom
|
||||||
|
jankeromnes
|
||||||
|
Jan Keromnes
|
||||||
|
Jan Odvarko
|
||||||
|
Jan Schär
|
||||||
|
Jan T. Sott
|
||||||
|
Jared Dean
|
||||||
|
Jared Forsyth
|
||||||
|
Jared Jacobs
|
||||||
|
Jason
|
||||||
|
Jason Barnabe
|
||||||
|
Jason Grout
|
||||||
|
Jason Heeris
|
||||||
|
Jason Johnston
|
||||||
|
Jason San Jose
|
||||||
|
Jason Siefken
|
||||||
|
Jayaprabhakar
|
||||||
|
Jay Contonio
|
||||||
|
Jaydeep Solanki
|
||||||
|
Jean Boussier
|
||||||
|
Jeff Blaisdell
|
||||||
|
Jeff Hanke
|
||||||
|
Jeff Jenkins
|
||||||
|
jeffkenton
|
||||||
|
Jeff Pickhardt
|
||||||
|
jem (graphite)
|
||||||
|
Jeremy Parmenter
|
||||||
|
Jim
|
||||||
|
Jim Avery
|
||||||
|
jkaplon
|
||||||
|
JobJob
|
||||||
|
jochenberger
|
||||||
|
Jochen Berger
|
||||||
|
Joel Einbinder
|
||||||
|
joelpinheiro
|
||||||
|
joewalsh
|
||||||
|
Johan Ask
|
||||||
|
Johannes
|
||||||
|
John Chen
|
||||||
|
John Connor
|
||||||
|
John-David Dalton
|
||||||
|
John Engler
|
||||||
|
John Lees-Miller
|
||||||
|
John Ryan
|
||||||
|
John Snelson
|
||||||
|
John Van Der Loo
|
||||||
|
Jon Ander Peñalba
|
||||||
|
Jonas Döbertin
|
||||||
|
Jonas Helfer
|
||||||
|
Jonathan Dierksen
|
||||||
|
Jonathan Hart
|
||||||
|
Jonathan Malmaud
|
||||||
|
Jon Gacnik
|
||||||
|
jongalloway
|
||||||
|
Jon Malmaud
|
||||||
|
Jon Sangster
|
||||||
|
Joo
|
||||||
|
Joost-Wim Boekesteijn
|
||||||
|
Joseph Pecoraro
|
||||||
|
Josh Barnes
|
||||||
|
Josh Cohen
|
||||||
|
Josh Soref
|
||||||
|
Joshua Newman
|
||||||
|
Josh Watzman
|
||||||
|
jots
|
||||||
|
Joy Zhong
|
||||||
|
jsoojeon
|
||||||
|
ju1ius
|
||||||
|
Juan Benavides Romero
|
||||||
|
Jucovschi Constantin
|
||||||
|
Juho Vuori
|
||||||
|
Julien CROUZET
|
||||||
|
Julien Rebetez
|
||||||
|
Justin Andresen
|
||||||
|
Justin Hileman
|
||||||
|
jwallers@gmail.com
|
||||||
|
kaniga
|
||||||
|
karevn
|
||||||
|
Karol
|
||||||
|
Kaushik Kulkarni
|
||||||
|
Kayur Patel
|
||||||
|
Kazuhito Hokamura
|
||||||
|
kcwiakala
|
||||||
|
Kees de Kooter
|
||||||
|
Kenan Christian Dimas
|
||||||
|
Ken Newman
|
||||||
|
ken restivo
|
||||||
|
Ken Rockot
|
||||||
|
Kevin Earls
|
||||||
|
Kevin Kwok
|
||||||
|
Kevin Muret
|
||||||
|
Kevin Sawicki
|
||||||
|
Kevin Ushey
|
||||||
|
Kier Darby
|
||||||
|
Klaus Silveira
|
||||||
|
Koh Zi Han, Cliff
|
||||||
|
komakino
|
||||||
|
Konstantin Lopuhin
|
||||||
|
koops
|
||||||
|
Kris Ciccarello
|
||||||
|
ks-ifware
|
||||||
|
kubelsmieci
|
||||||
|
kvncp
|
||||||
|
KwanEsq
|
||||||
|
Kyle Kelley
|
||||||
|
KyleMcNutt
|
||||||
|
LaKing
|
||||||
|
Lanfei
|
||||||
|
Lanny
|
||||||
|
laobubu
|
||||||
|
Laszlo Vidacs
|
||||||
|
leaf
|
||||||
|
leaf corcoran
|
||||||
|
Lemmon
|
||||||
|
Leo Baschy
|
||||||
|
Leonid Khachaturov
|
||||||
|
Leon Sorokin
|
||||||
|
Leonya Khachaturov
|
||||||
|
Liam Newman
|
||||||
|
Libo Cannici
|
||||||
|
Lior Goldberg
|
||||||
|
Lior Shub
|
||||||
|
LloydMilligan
|
||||||
|
LM
|
||||||
|
lochel
|
||||||
|
Lonnie Abelbeck
|
||||||
|
Lorenzo Simionato
|
||||||
|
Lorenzo Stoakes
|
||||||
|
Louis Mauchet
|
||||||
|
Luca Fabbri
|
||||||
|
Lucas Buchala
|
||||||
|
Luciano Longo
|
||||||
|
Luciano Santana
|
||||||
|
Lu Fangjian
|
||||||
|
Luke Browning
|
||||||
|
Luke Granger-Brown
|
||||||
|
Luke Stagner
|
||||||
|
lynschinzer
|
||||||
|
M1cha
|
||||||
|
Madhura Jayaratne
|
||||||
|
Maksim Lin
|
||||||
|
Maksym Taran
|
||||||
|
Malay Majithia
|
||||||
|
Manideep
|
||||||
|
Manuel Rego Casasnovas
|
||||||
|
Marat Dreizin
|
||||||
|
Marcel Gerber
|
||||||
|
Marcelo Camargo
|
||||||
|
Marc Espín
|
||||||
|
Marco Aurélio
|
||||||
|
Marco Munizaga
|
||||||
|
Marcus Bointon
|
||||||
|
Marek Rudnicki
|
||||||
|
Marijn Haverbeke
|
||||||
|
Mário Gonçalves
|
||||||
|
Mario Pietsch
|
||||||
|
Mark Anderson
|
||||||
|
Mark Boyes
|
||||||
|
Mark Dalgleish
|
||||||
|
Mark Hamstra
|
||||||
|
Mark Lentczner
|
||||||
|
Marko Bonaci
|
||||||
|
Mark Peace
|
||||||
|
Markus Bordihn
|
||||||
|
Markus Olsson
|
||||||
|
Martin Balek
|
||||||
|
Martín Gaitán
|
||||||
|
Martin Hasoň
|
||||||
|
Martin Hunt
|
||||||
|
Martin Laine
|
||||||
|
Martin Zagora
|
||||||
|
Mason Malone
|
||||||
|
Mateusz Paprocki
|
||||||
|
Mathias Bynens
|
||||||
|
mats cronqvist
|
||||||
|
Matt Gaide
|
||||||
|
Matthew Bauer
|
||||||
|
Matthew Beale
|
||||||
|
Matthew Casperson
|
||||||
|
matthewhayes
|
||||||
|
Matthew Rathbone
|
||||||
|
Matthew Suozzo
|
||||||
|
Matthias Bussonnier
|
||||||
|
Matthias BUSSONNIER
|
||||||
|
Mattia Astorino
|
||||||
|
Matt MacPherson
|
||||||
|
Matt McDonald
|
||||||
|
Matt Pass
|
||||||
|
Matt Sacks
|
||||||
|
mauricio
|
||||||
|
Maximilian Hils
|
||||||
|
Maxim Kraev
|
||||||
|
Max Kirsch
|
||||||
|
Max Schaefer
|
||||||
|
Max Wu
|
||||||
|
Max Xiantu
|
||||||
|
mbarkhau
|
||||||
|
McBrainy
|
||||||
|
mce2
|
||||||
|
Mélanie Chauvel
|
||||||
|
melpon
|
||||||
|
meshuamam
|
||||||
|
Metatheos
|
||||||
|
Micah Dubinko
|
||||||
|
Michael
|
||||||
|
Michael Goderbauer
|
||||||
|
Michael Grey
|
||||||
|
Michael Kaminsky
|
||||||
|
Michael Lehenbauer
|
||||||
|
Michael Wadman
|
||||||
|
Michael Walker
|
||||||
|
Michael Zhou
|
||||||
|
Michal Čihař
|
||||||
|
Michal Dorner
|
||||||
|
Michal Kapiczynski
|
||||||
|
Mighty Guava
|
||||||
|
Miguel Castillo
|
||||||
|
mihailik
|
||||||
|
Mika Andrianarijaona
|
||||||
|
Mike
|
||||||
|
Mike Bostock
|
||||||
|
Mike Brevoort
|
||||||
|
Mike Diaz
|
||||||
|
Mike Ivanov
|
||||||
|
Mike Kadin
|
||||||
|
Mike Kobit
|
||||||
|
Milan Szekely
|
||||||
|
MinRK
|
||||||
|
Miraculix87
|
||||||
|
misfo
|
||||||
|
mkaminsky11
|
||||||
|
mloginov
|
||||||
|
Moritz Schubotz (physikerwelt)
|
||||||
|
Moritz Schwörer
|
||||||
|
Moshe Wajnberg
|
||||||
|
mps
|
||||||
|
ms
|
||||||
|
mtaran-google
|
||||||
|
Mu-An ✌️ Chiou
|
||||||
|
Mu-An Chiou
|
||||||
|
mzabuawala
|
||||||
|
Narciso Jaramillo
|
||||||
|
Nathan Williams
|
||||||
|
ndr
|
||||||
|
Neil Anderson
|
||||||
|
neon-dev
|
||||||
|
nerbert
|
||||||
|
NetworkNode
|
||||||
|
nextrevision
|
||||||
|
ngn
|
||||||
|
nguillaumin
|
||||||
|
Ng Zhi An
|
||||||
|
Nicholas Bollweg
|
||||||
|
Nicholas Bollweg (Nick)
|
||||||
|
NickKolok
|
||||||
|
Nick Kreeger
|
||||||
|
Nick Small
|
||||||
|
Nicolas Chevobbe
|
||||||
|
Nicolas Kick
|
||||||
|
Nicolò Ribaudo
|
||||||
|
Niels van Groningen
|
||||||
|
nightwing
|
||||||
|
Nikita Beloglazov
|
||||||
|
Nikita Vasilyev
|
||||||
|
Nikolaj Kappler
|
||||||
|
Nikolay Kostov
|
||||||
|
nilp0inter
|
||||||
|
Nils Knappmeier
|
||||||
|
Nina Pypchenko
|
||||||
|
Nisarg Jhaveri
|
||||||
|
nlwillia
|
||||||
|
noragrossman
|
||||||
|
Norman Rzepka
|
||||||
|
Nouzbe
|
||||||
|
Oleksandr Yakovenko
|
||||||
|
Olivia Ytterbrink
|
||||||
|
Opender Singh
|
||||||
|
opl-
|
||||||
|
Oreoluwa Onatemowo
|
||||||
|
orionlee
|
||||||
|
oscar.lofwenhamn
|
||||||
|
Oskar Segersvärd
|
||||||
|
ossdev
|
||||||
|
overdodactyl
|
||||||
|
pablo
|
||||||
|
pabloferz
|
||||||
|
Pablo Zubieta
|
||||||
|
paddya
|
||||||
|
Page
|
||||||
|
paladox
|
||||||
|
Panupong Pasupat
|
||||||
|
paris
|
||||||
|
Paris
|
||||||
|
Paris Kasidiaris
|
||||||
|
Patil Arpith
|
||||||
|
Patrick Kettner
|
||||||
|
Patrick Stoica
|
||||||
|
Patrick Strawderman
|
||||||
|
Paul Garvin
|
||||||
|
Paul Ivanov
|
||||||
|
Paul Masson
|
||||||
|
Paul Schmidt
|
||||||
|
Pavel
|
||||||
|
Pavel Feldman
|
||||||
|
Pavel Petržela
|
||||||
|
Pavel Strashkin
|
||||||
|
Paweł Bartkiewicz
|
||||||
|
peteguhl
|
||||||
|
peter
|
||||||
|
Peter Flynn
|
||||||
|
peterkroon
|
||||||
|
Peter Kroon
|
||||||
|
Peter László
|
||||||
|
Phil DeJarnett
|
||||||
|
Philipp A
|
||||||
|
Philipp Markovics
|
||||||
|
Philip Stadermann
|
||||||
|
Pi Delport
|
||||||
|
Pierre Gerold
|
||||||
|
Pieter Ouwerkerk
|
||||||
|
Pontus Melke
|
||||||
|
prasanthj
|
||||||
|
Prasanth J
|
||||||
|
Prayag Verma
|
||||||
|
prendota
|
||||||
|
Prendota
|
||||||
|
Qiang Li
|
||||||
|
Radek Piórkowski
|
||||||
|
Rahul
|
||||||
|
Rahul Anand
|
||||||
|
ramwin1
|
||||||
|
Randall Mason
|
||||||
|
Randy Burden
|
||||||
|
Randy Edmunds
|
||||||
|
Randy Luecke
|
||||||
|
Raphael Amorim
|
||||||
|
Rasmus Erik Voel Jensen
|
||||||
|
Rasmus Schultz
|
||||||
|
raymondf
|
||||||
|
Raymond Hill
|
||||||
|
ray ratchup
|
||||||
|
Ray Ratchup
|
||||||
|
Remi Nyborg
|
||||||
|
Renaud Durlin
|
||||||
|
Reynold Xin
|
||||||
|
Richard Denton
|
||||||
|
Richard van der Meer
|
||||||
|
Richard Z.H. Wang
|
||||||
|
Rishi Goomar
|
||||||
|
Robert Brignull
|
||||||
|
Robert Crossfield
|
||||||
|
Robert Martin
|
||||||
|
Roberto Abdelkader Martínez Pérez
|
||||||
|
robertop23
|
||||||
|
Roberto Vidal
|
||||||
|
Robert Plummer
|
||||||
|
Roman Janusz
|
||||||
|
Rrandom
|
||||||
|
Rrrandom
|
||||||
|
Ruslan Osmanov
|
||||||
|
rvalavicius
|
||||||
|
Ryan Pangrle
|
||||||
|
Ryan Petrello
|
||||||
|
Ryan Prior
|
||||||
|
ryu-sato
|
||||||
|
sabaca
|
||||||
|
Sam Lee
|
||||||
|
Sam Rawlins
|
||||||
|
Samuel Ainsworth
|
||||||
|
Sam Wilson
|
||||||
|
sandeepshetty
|
||||||
|
Sander AKA Redsandro
|
||||||
|
Sander Verweij
|
||||||
|
santec
|
||||||
|
Sarah McAlear and Wenlin Zhang
|
||||||
|
Sascha Peilicke
|
||||||
|
Sasha Varlamov
|
||||||
|
satamas
|
||||||
|
satchmorun
|
||||||
|
sathyamoorthi
|
||||||
|
Saul Costa
|
||||||
|
S. Chris Colbert
|
||||||
|
SCLINIC\jdecker
|
||||||
|
Scott Aikin
|
||||||
|
Scott Feeney
|
||||||
|
Scott Goodhew
|
||||||
|
Seb35
|
||||||
|
Sebastian Wilzbach
|
||||||
|
Sebastian Zaha
|
||||||
|
Seren D
|
||||||
|
Sergey Goder
|
||||||
|
Sergey Tselovalnikov
|
||||||
|
Se-Won Kim
|
||||||
|
Shane Liesegang
|
||||||
|
shaund
|
||||||
|
shaun gilchrist
|
||||||
|
Shawn A
|
||||||
|
Shea Bunge
|
||||||
|
sheopory
|
||||||
|
Shil S
|
||||||
|
Shiv Deepak
|
||||||
|
Shmuel Englard
|
||||||
|
Shubham Jain
|
||||||
|
Siamak Mokhtari
|
||||||
|
Siddhartha Gunti
|
||||||
|
silverwind
|
||||||
|
Simon Edwards
|
||||||
|
sinkuu
|
||||||
|
snasa
|
||||||
|
soliton4
|
||||||
|
sonson
|
||||||
|
Sorab Bisht
|
||||||
|
spastorelli
|
||||||
|
srajanpaliwal
|
||||||
|
Stanislav Oaserele
|
||||||
|
stan-z
|
||||||
|
Stas Kobzar
|
||||||
|
Stefan Borsje
|
||||||
|
Steffen Beyer
|
||||||
|
Steffen Bruchmann
|
||||||
|
Steffen Kowalski
|
||||||
|
Stephane Moore
|
||||||
|
Stephen Lavelle
|
||||||
|
Steve Champagne
|
||||||
|
Steve Hoover
|
||||||
|
Steve O'Hara
|
||||||
|
stockiNail
|
||||||
|
stoskov
|
||||||
|
Stryder Crown
|
||||||
|
Stu Kennedy
|
||||||
|
Sungho Kim
|
||||||
|
sverweij
|
||||||
|
Taha Jahangir
|
||||||
|
takamori
|
||||||
|
Tako Schotanus
|
||||||
|
Takuji Shimokawa
|
||||||
|
Takuya Matsuyama
|
||||||
|
Tarmil
|
||||||
|
T. Brandon Ashley
|
||||||
|
TDaglis
|
||||||
|
Teja
|
||||||
|
tel
|
||||||
|
Tentone
|
||||||
|
tfjgeorge
|
||||||
|
Thaddee Tyl
|
||||||
|
thanasis
|
||||||
|
TheHowl
|
||||||
|
themrmax
|
||||||
|
think
|
||||||
|
Thomas Brouard
|
||||||
|
Thomas Dvornik
|
||||||
|
Thomas Kluyver
|
||||||
|
thomasmaclean
|
||||||
|
Thomas Schmid
|
||||||
|
Tim Alby
|
||||||
|
Tim Baumann
|
||||||
|
Tim Gates
|
||||||
|
Timothy Farrell
|
||||||
|
Timothy Gu
|
||||||
|
Timothy Hatcher
|
||||||
|
Tim van der Lippe
|
||||||
|
Tobias Bertelsen
|
||||||
|
TobiasBg
|
||||||
|
Todd Berman
|
||||||
|
Todd Kennedy
|
||||||
|
tokafew420
|
||||||
|
Tomas-A
|
||||||
|
Tomas Varaneckas
|
||||||
|
Tom Erik Støwer
|
||||||
|
Tom Klancer
|
||||||
|
Tom MacWright
|
||||||
|
Tom McLaughlin
|
||||||
|
Tony Jian
|
||||||
|
tophf
|
||||||
|
Torgeir Thoresen
|
||||||
|
totalamd
|
||||||
|
Travis Heppe
|
||||||
|
Triangle717
|
||||||
|
Tristan Tarrant
|
||||||
|
TSUYUSATO Kitsune
|
||||||
|
Tugrul Elmas
|
||||||
|
twifkak
|
||||||
|
Tyler Long
|
||||||
|
Tyler Makaro
|
||||||
|
Vadim Dyachenko
|
||||||
|
Vadzim Ramanenka
|
||||||
|
Vaibhav Sagar
|
||||||
|
vamshi.revu
|
||||||
|
VapidWorx
|
||||||
|
Vestimir Markov
|
||||||
|
vf
|
||||||
|
Victor Bocharsky
|
||||||
|
Vincent Woo
|
||||||
|
Volker Mische
|
||||||
|
vtripolitakis
|
||||||
|
wdouglashall
|
||||||
|
Weiyan Shao
|
||||||
|
wenli
|
||||||
|
Wes Cossick
|
||||||
|
Wesley Wiser
|
||||||
|
Weston Ruter
|
||||||
|
Will Binns-Smith
|
||||||
|
Will Dean
|
||||||
|
William Desportes
|
||||||
|
William Jamieson
|
||||||
|
William Stein
|
||||||
|
Willy
|
||||||
|
Wojtek Ptak
|
||||||
|
wonderboyjon
|
||||||
|
Wu Cheng-Han
|
||||||
|
Xavier Mendez
|
||||||
|
Yang Guo
|
||||||
|
Yassin N. Hassan
|
||||||
|
YNH Webdev
|
||||||
|
yoongu
|
||||||
|
Yunchi Luo
|
||||||
|
Yuvi Panda
|
||||||
|
Yvonnick Esnault
|
||||||
|
Zac Anger
|
||||||
|
Zachary Dremann
|
||||||
|
ZeeshanNoor
|
||||||
|
Zeno Rocha
|
||||||
|
Zhang Hao
|
||||||
|
Ziv
|
||||||
|
zoobestik
|
||||||
|
zziuni
|
||||||
|
魏鹏刚
|
||||||
1912
src/modules/_CodeMirror/CHANGELOG.md
Normal file
1912
src/modules/_CodeMirror/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
92
src/modules/_CodeMirror/CONTRIBUTING.md
Normal file
92
src/modules/_CodeMirror/CONTRIBUTING.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# How to contribute
|
||||||
|
|
||||||
|
- [Getting help](#getting-help)
|
||||||
|
- [Submitting bug reports](#submitting-bug-reports)
|
||||||
|
- [Contributing code](#contributing-code)
|
||||||
|
|
||||||
|
## Getting help
|
||||||
|
|
||||||
|
Community discussion, questions, and informal bug reporting is done on the
|
||||||
|
[discuss.CodeMirror forum](http://discuss.codemirror.net).
|
||||||
|
|
||||||
|
## Submitting bug reports
|
||||||
|
|
||||||
|
The preferred way to report bugs is to use the
|
||||||
|
[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
|
||||||
|
reporting a bug, read these pointers.
|
||||||
|
|
||||||
|
**Note:** The issue tracker is for *bugs*, not requests for help. Questions
|
||||||
|
should be asked on the
|
||||||
|
[discuss.CodeMirror forum](http://discuss.codemirror.net) instead.
|
||||||
|
|
||||||
|
### Reporting bugs effectively
|
||||||
|
|
||||||
|
- CodeMirror is maintained by volunteers. They don't owe you anything, so be
|
||||||
|
polite. Reports with an indignant or belligerent tone tend to be moved to the
|
||||||
|
bottom of the pile.
|
||||||
|
|
||||||
|
- Include information about **the browser in which the problem occurred**. Even
|
||||||
|
if you tested several browsers, and the problem occurred in all of them,
|
||||||
|
mention this fact in the bug report. Also include browser version numbers and
|
||||||
|
the operating system that you're on.
|
||||||
|
|
||||||
|
- Mention which release of CodeMirror you're using. Preferably, try also with
|
||||||
|
the current development snapshot, to ensure the problem has not already been
|
||||||
|
fixed.
|
||||||
|
|
||||||
|
- Mention very precisely what went wrong. "X is broken" is not a good bug
|
||||||
|
report. What did you expect to happen? What happened instead? Describe the
|
||||||
|
exact steps a maintainer has to take to make the problem occur. We can not
|
||||||
|
fix something that we can not observe.
|
||||||
|
|
||||||
|
- If the problem can not be reproduced in any of the demos included in the
|
||||||
|
CodeMirror distribution, please provide an HTML document that demonstrates
|
||||||
|
the problem. The best way to do this is to go to
|
||||||
|
[jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and
|
||||||
|
include the resulting link in your bug report.
|
||||||
|
|
||||||
|
## Contributing code
|
||||||
|
|
||||||
|
Note that we are not accepting any new addons or modes into the main
|
||||||
|
distribution. If you've written such a module, please distribute it as
|
||||||
|
a separate NPM package.
|
||||||
|
|
||||||
|
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
||||||
|
- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)
|
||||||
|
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
||||||
|
- Make your changes
|
||||||
|
- If your changes are easy to test or likely to regress, add tests.
|
||||||
|
Tests for the core go into `test/test.js`, some modes have their own
|
||||||
|
test suite under `mode/XXX/test.js`. Feel free to add new test
|
||||||
|
suites to modes that don't have one yet (be sure to link the new
|
||||||
|
tests into `test/index.html`).
|
||||||
|
- Follow the general code style of the rest of the project (see
|
||||||
|
below). Run `bin/lint` to verify that the linter is happy.
|
||||||
|
- Make sure all tests pass. Visit `test/index.html` in your browser to
|
||||||
|
run them.
|
||||||
|
- Submit a pull request
|
||||||
|
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
|
||||||
|
Don't put more than one feature/fix in a single pull request.
|
||||||
|
|
||||||
|
By contributing code to CodeMirror you
|
||||||
|
|
||||||
|
- agree to license the contributed code under CodeMirror's [MIT
|
||||||
|
license](https://codemirror.net/LICENSE).
|
||||||
|
|
||||||
|
- confirm that you have the right to contribute and license the code
|
||||||
|
in question. (Either you hold all rights on the code, or the rights
|
||||||
|
holder has explicitly granted the right to use it like this,
|
||||||
|
through a compatible open source license or through a direct
|
||||||
|
agreement with you.)
|
||||||
|
|
||||||
|
### Coding standards
|
||||||
|
|
||||||
|
- 2 spaces per indentation level, no tabs.
|
||||||
|
|
||||||
|
- Note that the linter (`bin/lint`) which is run after each commit
|
||||||
|
complains about unused variables and functions. Prefix their names
|
||||||
|
with an underscore to muffle it.
|
||||||
|
|
||||||
|
- CodeMirror does *not* follow JSHint or JSLint prescribed style.
|
||||||
|
Patches that try to 'fix' code to pass one of these linters will be
|
||||||
|
unceremoniously discarded.
|
||||||
21
src/modules/_CodeMirror/LICENSE
Normal file
21
src/modules/_CodeMirror/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
118
src/modules/_CodeMirror/README.md
Normal file
118
src/modules/_CodeMirror/README.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
# CodeMirror for Foundry VTT
|
||||||
|
This package includes the CodeMirror editor library. A lightweight editor that can be used as a drop-in replacement for a textarea. The editor can be configured with text highlighting for various languages.
|
||||||
|
|
||||||
|
This module also provides a small number of user configurable settings you can use when creating an editor instance to allow a degree of customization.
|
||||||
|
## Integration
|
||||||
|
Using the CM library as a dependency is very simple, this module already includes the following language modes:
|
||||||
|
|
||||||
|
```
|
||||||
|
javascript, css, xml, html, handlebars, markdown, toml, vue
|
||||||
|
```
|
||||||
|
|
||||||
|
Simply declare this module as a dependency by including the following in your manifest:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"name": "_CodeMirror",
|
||||||
|
"type": "module",
|
||||||
|
"manifest": "https://raw.githubusercontent.com/League-of-Foundry-Developers/codemirror-lib/master/module.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now create an instance of CM in any way outlined in the CM documnetation. The simplest method is to "upgrade" a `textarea` into a CM instance, by passing a reference to the `textarea` element to the `fromTextArea` method.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const editor = CodeMirror.fromTextArea(html.find("textarea")[0], {
|
||||||
|
mode: "javascript", // A mode like "javascript" or "css"
|
||||||
|
...CodeMirror.userSettings, // A special helper described later
|
||||||
|
lineNumbers: true, // CM has a number of settings you can configure
|
||||||
|
inputStyle: "contenteditable",
|
||||||
|
autofocus: true
|
||||||
|
});
|
||||||
|
```
|
||||||
|
This upgrades the `textarea` into a proper text editor.
|
||||||
|
|
||||||
|
CodeMirror "saves" the editor data back to the original `textarea`, however sometimes you need to explicitly tell it to save, such as immediately before submitting a form. This can be done by using the `save()` method on a CodeMirror instance. With the exampe above:
|
||||||
|
|
||||||
|
```js
|
||||||
|
editor.save();
|
||||||
|
```
|
||||||
|
|
||||||
|
This will ensure that the `textarea` has the same content as the editor. It's good to call this at some point before you parse the data of the form, allowing you to retrieve the data from the `textarea` as normal.
|
||||||
|
|
||||||
|
Another option is to save on every change, by attaching an event handler to the CM object:
|
||||||
|
|
||||||
|
```js
|
||||||
|
CodeMirror.fromTextArea(textarea, {
|
||||||
|
// options
|
||||||
|
}).on("change", (instance) => instance.save());
|
||||||
|
```
|
||||||
|
|
||||||
|
This may be less efficient for large inputs.
|
||||||
|
|
||||||
|
### CodeMirror.userSettings
|
||||||
|
This special getter has been added to the `CodeMirror` object, it returns an object of settings retrieved from Foundry's module settings. This includes a few editor configurations like tab type and size, or word wrap. These settings can be passed along with other options when creating an instance of a CodeMirror editor. The simplest method is to use the `...` spread operator to insert the key/value pairs of this object directly into the options:
|
||||||
|
|
||||||
|
```js
|
||||||
|
CodeMirror.fromTextArea(textarea, {
|
||||||
|
mode: "css",
|
||||||
|
...CodeMirror.userSettings,
|
||||||
|
lineNumbers: true, // etc.
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows the user to configer editor preferences globally, while giving you a simple way to include those settings in your instance of the editor.
|
||||||
|
|
||||||
|
# Original CodeMirror Readme
|
||||||
|
|
||||||
|
[](https://travis-ci.org/codemirror/CodeMirror)
|
||||||
|
[](https://www.npmjs.org/package/codemirror)
|
||||||
|
|
||||||
|
CodeMirror is a versatile text editor implemented in JavaScript for
|
||||||
|
the browser. It is specialized for editing code, and comes with over
|
||||||
|
100 language modes and various addons that implement more advanced
|
||||||
|
editing functionality. Every language comes with fully-featured code
|
||||||
|
and syntax highlighting to help with reading and editing complex code.
|
||||||
|
|
||||||
|
A rich programming API and a CSS theming system are available for
|
||||||
|
customizing CodeMirror to fit your application, and extending it with
|
||||||
|
new functionality.
|
||||||
|
|
||||||
|
You can find more information (and the
|
||||||
|
[manual](https://codemirror.net/doc/manual.html)) on the [project
|
||||||
|
page](https://codemirror.net). For questions and discussion, use the
|
||||||
|
[discussion forum](https://discuss.codemirror.net/).
|
||||||
|
|
||||||
|
See
|
||||||
|
[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
|
||||||
|
for contributing guidelines.
|
||||||
|
|
||||||
|
The CodeMirror community aims to be welcoming to everybody. We use the
|
||||||
|
[Contributor Covenant
|
||||||
|
(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of
|
||||||
|
conduct.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Either get the [zip file](https://codemirror.net/codemirror.zip) with
|
||||||
|
the latest version, or make sure you have [Node](https://nodejs.org/)
|
||||||
|
installed and run:
|
||||||
|
|
||||||
|
npm install codemirror
|
||||||
|
|
||||||
|
**NOTE**: This is the source repository for the library, and not the
|
||||||
|
distribution channel. Cloning it is not the recommended way to install
|
||||||
|
the library, and will in fact not work unless you also run the build
|
||||||
|
step.
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
|
|
||||||
|
To build the project, make sure you have Node.js installed (at least version 6)
|
||||||
|
and then `npm install`. To run, just open `index.html` in your
|
||||||
|
browser (you don't need to run a webserver). Run the tests with `npm test`.
|
||||||
1
src/modules/_CodeMirror/_CodeMirror.lock
Normal file
1
src/modules/_CodeMirror/_CodeMirror.lock
Normal file
@@ -0,0 +1 @@
|
|||||||
|
🔒
|
||||||
211
src/modules/_CodeMirror/addon/comment/comment.js
Normal file
211
src/modules/_CodeMirror/addon/comment/comment.js
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var noOptions = {};
|
||||||
|
var nonWS = /[^\s\u00a0]/;
|
||||||
|
var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos;
|
||||||
|
|
||||||
|
function firstNonWS(str) {
|
||||||
|
var found = str.search(nonWS);
|
||||||
|
return found == -1 ? 0 : found;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.commands.toggleComment = function(cm) {
|
||||||
|
cm.toggleComment();
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("toggleComment", function(options) {
|
||||||
|
if (!options) options = noOptions;
|
||||||
|
var cm = this;
|
||||||
|
var minLine = Infinity, ranges = this.listSelections(), mode = null;
|
||||||
|
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||||
|
var from = ranges[i].from(), to = ranges[i].to();
|
||||||
|
if (from.line >= minLine) continue;
|
||||||
|
if (to.line >= minLine) to = Pos(minLine, 0);
|
||||||
|
minLine = from.line;
|
||||||
|
if (mode == null) {
|
||||||
|
if (cm.uncomment(from, to, options)) mode = "un";
|
||||||
|
else { cm.lineComment(from, to, options); mode = "line"; }
|
||||||
|
} else if (mode == "un") {
|
||||||
|
cm.uncomment(from, to, options);
|
||||||
|
} else {
|
||||||
|
cm.lineComment(from, to, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rough heuristic to try and detect lines that are part of multi-line string
|
||||||
|
function probablyInsideString(cm, pos, line) {
|
||||||
|
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMode(cm, pos) {
|
||||||
|
var mode = cm.getMode()
|
||||||
|
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("lineComment", function(from, to, options) {
|
||||||
|
if (!options) options = noOptions;
|
||||||
|
var self = this, mode = getMode(self, from);
|
||||||
|
var firstLine = self.getLine(from.line);
|
||||||
|
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
|
||||||
|
|
||||||
|
var commentString = options.lineComment || mode.lineComment;
|
||||||
|
if (!commentString) {
|
||||||
|
if (options.blockCommentStart || mode.blockCommentStart) {
|
||||||
|
options.fullLines = true;
|
||||||
|
self.blockComment(from, to, options);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
|
||||||
|
var pad = options.padding == null ? " " : options.padding;
|
||||||
|
var blankLines = options.commentBlankLines || from.line == to.line;
|
||||||
|
|
||||||
|
self.operation(function() {
|
||||||
|
if (options.indent) {
|
||||||
|
var baseString = null;
|
||||||
|
for (var i = from.line; i < end; ++i) {
|
||||||
|
var line = self.getLine(i);
|
||||||
|
var whitespace = line.slice(0, firstNonWS(line));
|
||||||
|
if (baseString == null || baseString.length > whitespace.length) {
|
||||||
|
baseString = whitespace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = from.line; i < end; ++i) {
|
||||||
|
var line = self.getLine(i), cut = baseString.length;
|
||||||
|
if (!blankLines && !nonWS.test(line)) continue;
|
||||||
|
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
|
||||||
|
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i = from.line; i < end; ++i) {
|
||||||
|
if (blankLines || nonWS.test(self.getLine(i)))
|
||||||
|
self.replaceRange(commentString + pad, Pos(i, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("blockComment", function(from, to, options) {
|
||||||
|
if (!options) options = noOptions;
|
||||||
|
var self = this, mode = getMode(self, from);
|
||||||
|
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||||
|
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||||
|
if (!startString || !endString) {
|
||||||
|
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
|
||||||
|
self.lineComment(from, to, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
|
||||||
|
|
||||||
|
var end = Math.min(to.line, self.lastLine());
|
||||||
|
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
|
||||||
|
|
||||||
|
var pad = options.padding == null ? " " : options.padding;
|
||||||
|
if (from.line > end) return;
|
||||||
|
|
||||||
|
self.operation(function() {
|
||||||
|
if (options.fullLines != false) {
|
||||||
|
var lastLineHasText = nonWS.test(self.getLine(end));
|
||||||
|
self.replaceRange(pad + endString, Pos(end));
|
||||||
|
self.replaceRange(startString + pad, Pos(from.line, 0));
|
||||||
|
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||||
|
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
|
||||||
|
if (i != end || lastLineHasText)
|
||||||
|
self.replaceRange(lead + pad, Pos(i, 0));
|
||||||
|
} else {
|
||||||
|
var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected()
|
||||||
|
self.replaceRange(endString, to);
|
||||||
|
if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to)
|
||||||
|
self.replaceRange(startString, from);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("uncomment", function(from, to, options) {
|
||||||
|
if (!options) options = noOptions;
|
||||||
|
var self = this, mode = getMode(self, from);
|
||||||
|
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
|
||||||
|
|
||||||
|
// Try finding line comments
|
||||||
|
var lineString = options.lineComment || mode.lineComment, lines = [];
|
||||||
|
var pad = options.padding == null ? " " : options.padding, didSomething;
|
||||||
|
lineComment: {
|
||||||
|
if (!lineString) break lineComment;
|
||||||
|
for (var i = start; i <= end; ++i) {
|
||||||
|
var line = self.getLine(i);
|
||||||
|
var found = line.indexOf(lineString);
|
||||||
|
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
|
||||||
|
if (found == -1 && nonWS.test(line)) break lineComment;
|
||||||
|
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
||||||
|
lines.push(line);
|
||||||
|
}
|
||||||
|
self.operation(function() {
|
||||||
|
for (var i = start; i <= end; ++i) {
|
||||||
|
var line = lines[i - start];
|
||||||
|
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
|
||||||
|
if (pos < 0) continue;
|
||||||
|
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
|
||||||
|
didSomething = true;
|
||||||
|
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (didSomething) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try block comments
|
||||||
|
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||||
|
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||||
|
if (!startString || !endString) return false;
|
||||||
|
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||||
|
var startLine = self.getLine(start), open = startLine.indexOf(startString)
|
||||||
|
if (open == -1) return false
|
||||||
|
var endLine = end == start ? startLine : self.getLine(end)
|
||||||
|
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
|
||||||
|
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
|
||||||
|
if (close == -1 ||
|
||||||
|
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
|
||||||
|
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
|
||||||
|
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Avoid killing block comments completely outside the selection.
|
||||||
|
// Positions of the last startString before the start of the selection, and the first endString after it.
|
||||||
|
var lastStart = startLine.lastIndexOf(startString, from.ch);
|
||||||
|
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
|
||||||
|
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
|
||||||
|
// Positions of the first endString after the end of the selection, and the last startString before it.
|
||||||
|
firstEnd = endLine.indexOf(endString, to.ch);
|
||||||
|
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
|
||||||
|
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
|
||||||
|
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
|
||||||
|
|
||||||
|
self.operation(function() {
|
||||||
|
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
||||||
|
Pos(end, close + endString.length));
|
||||||
|
var openEnd = open + startString.length;
|
||||||
|
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
|
||||||
|
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
|
||||||
|
if (lead) for (var i = start + 1; i <= end; ++i) {
|
||||||
|
var line = self.getLine(i), found = line.indexOf(lead);
|
||||||
|
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
|
||||||
|
var foundEnd = found + lead.length;
|
||||||
|
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
|
||||||
|
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
114
src/modules/_CodeMirror/addon/comment/continuecomment.js
Normal file
114
src/modules/_CodeMirror/addon/comment/continuecomment.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
var nonspace = /\S/g;
|
||||||
|
var repeat = String.prototype.repeat || function (n) { return Array(n + 1).join(this); };
|
||||||
|
function continueComment(cm) {
|
||||||
|
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
var ranges = cm.listSelections(), mode, inserts = [];
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var pos = ranges[i].head
|
||||||
|
if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
|
||||||
|
var modeHere = cm.getModeAt(pos)
|
||||||
|
if (!mode) mode = modeHere;
|
||||||
|
else if (mode != modeHere) return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var insert = null, line, found;
|
||||||
|
var blockStart = mode.blockCommentStart, lineCmt = mode.lineComment;
|
||||||
|
if (blockStart && mode.blockCommentContinue) {
|
||||||
|
line = cm.getLine(pos.line);
|
||||||
|
var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length);
|
||||||
|
// 1. if this block comment ended
|
||||||
|
// 2. if this is actually inside a line comment
|
||||||
|
if (end != -1 && end == pos.ch - mode.blockCommentEnd.length ||
|
||||||
|
lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 &&
|
||||||
|
/\bcomment\b/.test(cm.getTokenTypeAt({line: pos.line, ch: found + 1}))) {
|
||||||
|
// ...then don't continue it
|
||||||
|
} else if (pos.ch >= blockStart.length &&
|
||||||
|
(found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 &&
|
||||||
|
found > end) {
|
||||||
|
// reuse the existing leading spaces/tabs/mixed
|
||||||
|
// or build the correct indent using CM's tab/indent options
|
||||||
|
if (nonspaceAfter(0, line) >= found) {
|
||||||
|
insert = line.slice(0, found);
|
||||||
|
} else {
|
||||||
|
var tabSize = cm.options.tabSize, numTabs;
|
||||||
|
found = CodeMirror.countColumn(line, found, tabSize);
|
||||||
|
insert = !cm.options.indentWithTabs ? repeat.call(" ", found) :
|
||||||
|
repeat.call("\t", (numTabs = Math.floor(found / tabSize))) +
|
||||||
|
repeat.call(" ", found - tabSize * numTabs);
|
||||||
|
}
|
||||||
|
} else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 &&
|
||||||
|
found <= pos.ch &&
|
||||||
|
found <= nonspaceAfter(0, line)) {
|
||||||
|
insert = line.slice(0, found);
|
||||||
|
}
|
||||||
|
if (insert != null) insert += mode.blockCommentContinue
|
||||||
|
}
|
||||||
|
if (insert == null && lineCmt && continueLineCommentEnabled(cm)) {
|
||||||
|
if (line == null) line = cm.getLine(pos.line);
|
||||||
|
found = line.indexOf(lineCmt);
|
||||||
|
// cursor at pos 0, line comment also at pos 0 => shift it down, don't continue
|
||||||
|
if (!pos.ch && !found) insert = "";
|
||||||
|
// continue only if the line starts with an optional space + line comment
|
||||||
|
else if (found > -1 && nonspaceAfter(0, line) >= found) {
|
||||||
|
// don't continue if there's only space(s) after cursor or the end of the line
|
||||||
|
insert = nonspaceAfter(pos.ch, line) > -1;
|
||||||
|
// but always continue if the next line starts with a line comment too
|
||||||
|
if (!insert) {
|
||||||
|
var next = cm.getLine(pos.line + 1) || '',
|
||||||
|
nextFound = next.indexOf(lineCmt);
|
||||||
|
insert = nextFound > -1 && nonspaceAfter(0, next) >= nextFound || null;
|
||||||
|
}
|
||||||
|
if (insert) {
|
||||||
|
insert = line.slice(0, found) + lineCmt +
|
||||||
|
line.slice(found + lineCmt.length).match(/^\s*/)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (insert == null) return CodeMirror.Pass;
|
||||||
|
inserts[i] = "\n" + insert;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var i = ranges.length - 1; i >= 0; i--)
|
||||||
|
cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function nonspaceAfter(ch, str) {
|
||||||
|
nonspace.lastIndex = ch;
|
||||||
|
var m = nonspace.exec(str);
|
||||||
|
return m ? m.index : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueLineCommentEnabled(cm) {
|
||||||
|
var opt = cm.getOption("continueComments");
|
||||||
|
if (opt && typeof opt == "object")
|
||||||
|
return opt.continueLineComment !== false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
|
||||||
|
if (prev && prev != CodeMirror.Init)
|
||||||
|
cm.removeKeyMap("continueComment");
|
||||||
|
if (val) {
|
||||||
|
var key = "Enter";
|
||||||
|
if (typeof val == "string")
|
||||||
|
key = val;
|
||||||
|
else if (typeof val == "object" && val.key)
|
||||||
|
key = val.key;
|
||||||
|
var map = {name: "continueComment"};
|
||||||
|
map[key] = continueComment;
|
||||||
|
cm.addKeyMap(map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
32
src/modules/_CodeMirror/addon/dialog/dialog.css
Normal file
32
src/modules/_CodeMirror/addon/dialog/dialog.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.CodeMirror-dialog {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; right: 0;
|
||||||
|
background: inherit;
|
||||||
|
z-index: 15;
|
||||||
|
padding: .1em .8em;
|
||||||
|
overflow: hidden;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-dialog-top {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-dialog-bottom {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-dialog input {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 20em;
|
||||||
|
color: inherit;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-dialog button {
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
163
src/modules/_CodeMirror/addon/dialog/dialog.js
Normal file
163
src/modules/_CodeMirror/addon/dialog/dialog.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
function dialogDiv(cm, template, bottom) {
|
||||||
|
var wrap = cm.getWrapperElement();
|
||||||
|
var dialog;
|
||||||
|
dialog = wrap.appendChild(document.createElement("div"));
|
||||||
|
if (bottom)
|
||||||
|
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
|
||||||
|
else
|
||||||
|
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
|
||||||
|
|
||||||
|
if (typeof template == "string") {
|
||||||
|
dialog.innerHTML = template;
|
||||||
|
} else { // Assuming it's a detached DOM element.
|
||||||
|
dialog.appendChild(template);
|
||||||
|
}
|
||||||
|
CodeMirror.addClass(wrap, 'dialog-opened');
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeNotification(cm, newVal) {
|
||||||
|
if (cm.state.currentNotificationClose)
|
||||||
|
cm.state.currentNotificationClose();
|
||||||
|
cm.state.currentNotificationClose = newVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
||||||
|
if (!options) options = {};
|
||||||
|
|
||||||
|
closeNotification(this, null);
|
||||||
|
|
||||||
|
var dialog = dialogDiv(this, template, options.bottom);
|
||||||
|
var closed = false, me = this;
|
||||||
|
function close(newVal) {
|
||||||
|
if (typeof newVal == 'string') {
|
||||||
|
inp.value = newVal;
|
||||||
|
} else {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||||
|
dialog.parentNode.removeChild(dialog);
|
||||||
|
me.focus();
|
||||||
|
|
||||||
|
if (options.onClose) options.onClose(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inp = dialog.getElementsByTagName("input")[0], button;
|
||||||
|
if (inp) {
|
||||||
|
inp.focus();
|
||||||
|
|
||||||
|
if (options.value) {
|
||||||
|
inp.value = options.value;
|
||||||
|
if (options.selectValueOnOpen !== false) {
|
||||||
|
inp.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.onInput)
|
||||||
|
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
|
||||||
|
if (options.onKeyUp)
|
||||||
|
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
|
||||||
|
|
||||||
|
CodeMirror.on(inp, "keydown", function(e) {
|
||||||
|
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
||||||
|
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
|
||||||
|
inp.blur();
|
||||||
|
CodeMirror.e_stop(e);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
if (e.keyCode == 13) callback(inp.value, e);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
|
||||||
|
if (evt.relatedTarget !== null) close();
|
||||||
|
});
|
||||||
|
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
||||||
|
CodeMirror.on(button, "click", function() {
|
||||||
|
close();
|
||||||
|
me.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
|
||||||
|
|
||||||
|
button.focus();
|
||||||
|
}
|
||||||
|
return close;
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
||||||
|
closeNotification(this, null);
|
||||||
|
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||||
|
var buttons = dialog.getElementsByTagName("button");
|
||||||
|
var closed = false, me = this, blurring = 1;
|
||||||
|
function close() {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||||
|
dialog.parentNode.removeChild(dialog);
|
||||||
|
me.focus();
|
||||||
|
}
|
||||||
|
buttons[0].focus();
|
||||||
|
for (var i = 0; i < buttons.length; ++i) {
|
||||||
|
var b = buttons[i];
|
||||||
|
(function(callback) {
|
||||||
|
CodeMirror.on(b, "click", function(e) {
|
||||||
|
CodeMirror.e_preventDefault(e);
|
||||||
|
close();
|
||||||
|
if (callback) callback(me);
|
||||||
|
});
|
||||||
|
})(callbacks[i]);
|
||||||
|
CodeMirror.on(b, "blur", function() {
|
||||||
|
--blurring;
|
||||||
|
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
||||||
|
});
|
||||||
|
CodeMirror.on(b, "focus", function() { ++blurring; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* openNotification
|
||||||
|
* Opens a notification, that can be closed with an optional timer
|
||||||
|
* (default 5000ms timer) and always closes on click.
|
||||||
|
*
|
||||||
|
* If a notification is opened while another is opened, it will close the
|
||||||
|
* currently opened one and open the new one immediately.
|
||||||
|
*/
|
||||||
|
CodeMirror.defineExtension("openNotification", function(template, options) {
|
||||||
|
closeNotification(this, close);
|
||||||
|
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||||
|
var closed = false, doneTimer;
|
||||||
|
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
clearTimeout(doneTimer);
|
||||||
|
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||||
|
dialog.parentNode.removeChild(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.on(dialog, 'click', function(e) {
|
||||||
|
CodeMirror.e_preventDefault(e);
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (duration)
|
||||||
|
doneTimer = setTimeout(close, duration);
|
||||||
|
|
||||||
|
return close;
|
||||||
|
});
|
||||||
|
});
|
||||||
47
src/modules/_CodeMirror/addon/display/autorefresh.js
Normal file
47
src/modules/_CodeMirror/addon/display/autorefresh.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"))
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod)
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror)
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
|
||||||
|
if (cm.state.autoRefresh) {
|
||||||
|
stopListening(cm, cm.state.autoRefresh)
|
||||||
|
cm.state.autoRefresh = null
|
||||||
|
}
|
||||||
|
if (val && cm.display.wrapper.offsetHeight == 0)
|
||||||
|
startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
|
||||||
|
})
|
||||||
|
|
||||||
|
function startListening(cm, state) {
|
||||||
|
function check() {
|
||||||
|
if (cm.display.wrapper.offsetHeight) {
|
||||||
|
stopListening(cm, state)
|
||||||
|
if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
|
||||||
|
cm.refresh()
|
||||||
|
} else {
|
||||||
|
state.timeout = setTimeout(check, state.delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.timeout = setTimeout(check, state.delay)
|
||||||
|
state.hurry = function() {
|
||||||
|
clearTimeout(state.timeout)
|
||||||
|
state.timeout = setTimeout(check, 50)
|
||||||
|
}
|
||||||
|
CodeMirror.on(window, "mouseup", state.hurry)
|
||||||
|
CodeMirror.on(window, "keyup", state.hurry)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopListening(_cm, state) {
|
||||||
|
clearTimeout(state.timeout)
|
||||||
|
CodeMirror.off(window, "mouseup", state.hurry)
|
||||||
|
CodeMirror.off(window, "keyup", state.hurry)
|
||||||
|
}
|
||||||
|
});
|
||||||
6
src/modules/_CodeMirror/addon/display/fullscreen.css
Normal file
6
src/modules/_CodeMirror/addon/display/fullscreen.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.CodeMirror-fullscreen {
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
height: auto;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
41
src/modules/_CodeMirror/addon/display/fullscreen.js
Normal file
41
src/modules/_CodeMirror/addon/display/fullscreen.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
|
||||||
|
if (old == CodeMirror.Init) old = false;
|
||||||
|
if (!old == !val) return;
|
||||||
|
if (val) setFullscreen(cm);
|
||||||
|
else setNormal(cm);
|
||||||
|
});
|
||||||
|
|
||||||
|
function setFullscreen(cm) {
|
||||||
|
var wrap = cm.getWrapperElement();
|
||||||
|
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
|
||||||
|
width: wrap.style.width, height: wrap.style.height};
|
||||||
|
wrap.style.width = "";
|
||||||
|
wrap.style.height = "auto";
|
||||||
|
wrap.className += " CodeMirror-fullscreen";
|
||||||
|
document.documentElement.style.overflow = "hidden";
|
||||||
|
cm.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNormal(cm) {
|
||||||
|
var wrap = cm.getWrapperElement();
|
||||||
|
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
|
||||||
|
document.documentElement.style.overflow = "";
|
||||||
|
var info = cm.state.fullScreenRestore;
|
||||||
|
wrap.style.width = info.width; wrap.style.height = info.height;
|
||||||
|
window.scrollTo(info.scrollLeft, info.scrollTop);
|
||||||
|
cm.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
129
src/modules/_CodeMirror/addon/display/panel.js
Normal file
129
src/modules/_CodeMirror/addon/display/panel.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function (mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function (CodeMirror) {
|
||||||
|
CodeMirror.defineExtension("addPanel", function (node, options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (!this.state.panels) initPanels(this);
|
||||||
|
|
||||||
|
var info = this.state.panels;
|
||||||
|
var wrapper = info.wrapper;
|
||||||
|
var cmWrapper = this.getWrapperElement();
|
||||||
|
var replace = options.replace instanceof Panel && !options.replace.cleared;
|
||||||
|
|
||||||
|
if (options.after instanceof Panel && !options.after.cleared) {
|
||||||
|
wrapper.insertBefore(node, options.before.node.nextSibling);
|
||||||
|
} else if (options.before instanceof Panel && !options.before.cleared) {
|
||||||
|
wrapper.insertBefore(node, options.before.node);
|
||||||
|
} else if (replace) {
|
||||||
|
wrapper.insertBefore(node, options.replace.node);
|
||||||
|
options.replace.clear(true);
|
||||||
|
} else if (options.position == "bottom") {
|
||||||
|
wrapper.appendChild(node);
|
||||||
|
} else if (options.position == "before-bottom") {
|
||||||
|
wrapper.insertBefore(node, cmWrapper.nextSibling);
|
||||||
|
} else if (options.position == "after-top") {
|
||||||
|
wrapper.insertBefore(node, cmWrapper);
|
||||||
|
} else {
|
||||||
|
wrapper.insertBefore(node, wrapper.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var height = (options && options.height) || node.offsetHeight;
|
||||||
|
|
||||||
|
var panel = new Panel(this, node, options, height);
|
||||||
|
info.panels.push(panel);
|
||||||
|
|
||||||
|
this.setSize();
|
||||||
|
if (options.stable && isAtTop(this, node))
|
||||||
|
this.scrollTo(null, this.getScrollInfo().top + height);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
});
|
||||||
|
|
||||||
|
function Panel(cm, node, options, height) {
|
||||||
|
this.cm = cm;
|
||||||
|
this.node = node;
|
||||||
|
this.options = options;
|
||||||
|
this.height = height;
|
||||||
|
this.cleared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when skipRemove is true, clear() was called from addPanel().
|
||||||
|
* Thus removePanels() should not be called (issue 5518) */
|
||||||
|
Panel.prototype.clear = function (skipRemove) {
|
||||||
|
if (this.cleared) return;
|
||||||
|
this.cleared = true;
|
||||||
|
var info = this.cm.state.panels;
|
||||||
|
info.panels.splice(info.panels.indexOf(this), 1);
|
||||||
|
this.cm.setSize();
|
||||||
|
if (this.options.stable && isAtTop(this.cm, this.node))
|
||||||
|
this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
|
||||||
|
info.wrapper.removeChild(this.node);
|
||||||
|
if (info.panels.length == 0 && !skipRemove) removePanels(this.cm);
|
||||||
|
};
|
||||||
|
|
||||||
|
Panel.prototype.changed = function () {
|
||||||
|
this.height = this.node.getBoundingClientRect().height;
|
||||||
|
this.cm.setSize();
|
||||||
|
};
|
||||||
|
|
||||||
|
function initPanels(cm) {
|
||||||
|
var wrap = cm.getWrapperElement();
|
||||||
|
var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
|
||||||
|
var height = parseInt(style.height);
|
||||||
|
var info = cm.state.panels = {
|
||||||
|
setHeight: wrap.style.height,
|
||||||
|
panels: [],
|
||||||
|
wrapper: document.createElement("div")
|
||||||
|
};
|
||||||
|
wrap.parentNode.insertBefore(info.wrapper, wrap);
|
||||||
|
var hasFocus = cm.hasFocus();
|
||||||
|
info.wrapper.appendChild(wrap);
|
||||||
|
if (hasFocus) cm.focus();
|
||||||
|
|
||||||
|
cm._setSize = cm.setSize;
|
||||||
|
if (height != null) cm.setSize = function (width, newHeight) {
|
||||||
|
if (!newHeight) newHeight = info.wrapper.offsetHeight;
|
||||||
|
info.setHeight = newHeight;
|
||||||
|
if (typeof newHeight != "number") {
|
||||||
|
var px = /^(\d+\.?\d*)px$/.exec(newHeight);
|
||||||
|
if (px) {
|
||||||
|
newHeight = Number(px[1]);
|
||||||
|
} else {
|
||||||
|
info.wrapper.style.height = newHeight;
|
||||||
|
newHeight = info.wrapper.offsetHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var editorheight = newHeight - info.panels
|
||||||
|
.map(function (p) { return p.node.getBoundingClientRect().height; })
|
||||||
|
.reduce(function (a, b) { return a + b; }, 0);
|
||||||
|
cm._setSize(width, editorheight);
|
||||||
|
height = newHeight;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePanels(cm) {
|
||||||
|
var info = cm.state.panels;
|
||||||
|
cm.state.panels = null;
|
||||||
|
|
||||||
|
var wrap = cm.getWrapperElement();
|
||||||
|
info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
|
||||||
|
wrap.style.height = info.setHeight;
|
||||||
|
cm.setSize = cm._setSize;
|
||||||
|
cm.setSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAtTop(cm, dom) {
|
||||||
|
for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)
|
||||||
|
if (sibling == cm.getWrapperElement()) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
});
|
||||||
78
src/modules/_CodeMirror/addon/display/placeholder.js
Normal file
78
src/modules/_CodeMirror/addon/display/placeholder.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
|
||||||
|
var prev = old && old != CodeMirror.Init;
|
||||||
|
if (val && !prev) {
|
||||||
|
cm.on("blur", onBlur);
|
||||||
|
cm.on("change", onChange);
|
||||||
|
cm.on("swapDoc", onChange);
|
||||||
|
CodeMirror.on(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose = function() { onComposition(cm) })
|
||||||
|
onChange(cm);
|
||||||
|
} else if (!val && prev) {
|
||||||
|
cm.off("blur", onBlur);
|
||||||
|
cm.off("change", onChange);
|
||||||
|
cm.off("swapDoc", onChange);
|
||||||
|
CodeMirror.off(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose)
|
||||||
|
clearPlaceholder(cm);
|
||||||
|
var wrapper = cm.getWrapperElement();
|
||||||
|
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val && !cm.hasFocus()) onBlur(cm);
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearPlaceholder(cm) {
|
||||||
|
if (cm.state.placeholder) {
|
||||||
|
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
|
||||||
|
cm.state.placeholder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setPlaceholder(cm) {
|
||||||
|
clearPlaceholder(cm);
|
||||||
|
var elt = cm.state.placeholder = document.createElement("pre");
|
||||||
|
elt.style.cssText = "height: 0; overflow: visible";
|
||||||
|
elt.style.direction = cm.getOption("direction");
|
||||||
|
elt.className = "CodeMirror-placeholder CodeMirror-line-like";
|
||||||
|
var placeHolder = cm.getOption("placeholder")
|
||||||
|
if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
|
||||||
|
elt.appendChild(placeHolder)
|
||||||
|
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onComposition(cm) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var empty = false
|
||||||
|
if (cm.lineCount() == 1) {
|
||||||
|
var input = cm.getInputField()
|
||||||
|
empty = input.nodeName == "TEXTAREA" ? !cm.getLine(0).length
|
||||||
|
: !/[^\u200b]/.test(input.querySelector(".CodeMirror-line").textContent)
|
||||||
|
}
|
||||||
|
if (empty) setPlaceholder(cm)
|
||||||
|
else clearPlaceholder(cm)
|
||||||
|
}, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlur(cm) {
|
||||||
|
if (isEmpty(cm)) setPlaceholder(cm);
|
||||||
|
}
|
||||||
|
function onChange(cm) {
|
||||||
|
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
|
||||||
|
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
|
||||||
|
|
||||||
|
if (empty) setPlaceholder(cm);
|
||||||
|
else clearPlaceholder(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty(cm) {
|
||||||
|
return (cm.lineCount() === 1) && (cm.getLine(0) === "");
|
||||||
|
}
|
||||||
|
});
|
||||||
51
src/modules/_CodeMirror/addon/display/rulers.js
Normal file
51
src/modules/_CodeMirror/addon/display/rulers.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("rulers", false, function(cm, val) {
|
||||||
|
if (cm.state.rulerDiv) {
|
||||||
|
cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)
|
||||||
|
cm.state.rulerDiv = null
|
||||||
|
cm.off("refresh", drawRulers)
|
||||||
|
}
|
||||||
|
if (val && val.length) {
|
||||||
|
cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace)
|
||||||
|
cm.state.rulerDiv.className = "CodeMirror-rulers"
|
||||||
|
drawRulers(cm)
|
||||||
|
cm.on("refresh", drawRulers)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function drawRulers(cm) {
|
||||||
|
cm.state.rulerDiv.textContent = ""
|
||||||
|
var val = cm.getOption("rulers");
|
||||||
|
var cw = cm.defaultCharWidth();
|
||||||
|
var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
|
||||||
|
cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px";
|
||||||
|
for (var i = 0; i < val.length; i++) {
|
||||||
|
var elt = document.createElement("div");
|
||||||
|
elt.className = "CodeMirror-ruler";
|
||||||
|
var col, conf = val[i];
|
||||||
|
if (typeof conf == "number") {
|
||||||
|
col = conf;
|
||||||
|
} else {
|
||||||
|
col = conf.column;
|
||||||
|
if (conf.className) elt.className += " " + conf.className;
|
||||||
|
if (conf.color) elt.style.borderColor = conf.color;
|
||||||
|
if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
|
||||||
|
if (conf.width) elt.style.borderLeftWidth = conf.width;
|
||||||
|
}
|
||||||
|
elt.style.left = (left + col * cw) + "px";
|
||||||
|
cm.state.rulerDiv.appendChild(elt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
191
src/modules/_CodeMirror/addon/edit/closebrackets.js
Normal file
191
src/modules/_CodeMirror/addon/edit/closebrackets.js
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
var defaults = {
|
||||||
|
pairs: "()[]{}''\"\"",
|
||||||
|
closeBefore: ")]}'\":;>",
|
||||||
|
triples: "",
|
||||||
|
explode: "[]{}"
|
||||||
|
};
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
cm.removeKeyMap(keyMap);
|
||||||
|
cm.state.closeBrackets = null;
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
ensureBound(getOption(val, "pairs"))
|
||||||
|
cm.state.closeBrackets = val;
|
||||||
|
cm.addKeyMap(keyMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getOption(conf, name) {
|
||||||
|
if (name == "pairs" && typeof conf == "string") return conf;
|
||||||
|
if (typeof conf == "object" && conf[name] != null) return conf[name];
|
||||||
|
return defaults[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||||
|
function ensureBound(chars) {
|
||||||
|
for (var i = 0; i < chars.length; i++) {
|
||||||
|
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||||
|
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ensureBound(defaults.pairs + "`")
|
||||||
|
|
||||||
|
function handler(ch) {
|
||||||
|
return function(cm) { return handleChar(cm, ch); };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfig(cm) {
|
||||||
|
var deflt = cm.state.closeBrackets;
|
||||||
|
if (!deflt || deflt.override) return deflt;
|
||||||
|
var mode = cm.getModeAt(cm.getCursor());
|
||||||
|
return mode.closeBrackets || deflt;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBackspace(cm) {
|
||||||
|
var conf = getConfig(cm);
|
||||||
|
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var pairs = getOption(conf, "pairs");
|
||||||
|
var ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||||
|
var around = charsAround(cm, ranges[i].head);
|
||||||
|
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||||
|
}
|
||||||
|
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||||
|
var cur = ranges[i].head;
|
||||||
|
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEnter(cm) {
|
||||||
|
var conf = getConfig(cm);
|
||||||
|
var explode = conf && getOption(conf, "explode");
|
||||||
|
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||||
|
var around = charsAround(cm, ranges[i].head);
|
||||||
|
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||||
|
}
|
||||||
|
cm.operation(function() {
|
||||||
|
var linesep = cm.lineSeparator() || "\n";
|
||||||
|
cm.replaceSelection(linesep + linesep, null);
|
||||||
|
cm.execCommand("goCharLeft");
|
||||||
|
ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var line = ranges[i].head.line;
|
||||||
|
cm.indentLine(line, null, true);
|
||||||
|
cm.indentLine(line + 1, null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function contractSelection(sel) {
|
||||||
|
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
|
||||||
|
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
|
||||||
|
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChar(cm, ch) {
|
||||||
|
var conf = getConfig(cm);
|
||||||
|
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var pairs = getOption(conf, "pairs");
|
||||||
|
var pos = pairs.indexOf(ch);
|
||||||
|
if (pos == -1) return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var closeBefore = getOption(conf,"closeBefore");
|
||||||
|
|
||||||
|
var triples = getOption(conf, "triples");
|
||||||
|
|
||||||
|
var identical = pairs.charAt(pos + 1) == ch;
|
||||||
|
var ranges = cm.listSelections();
|
||||||
|
var opening = pos % 2 == 0;
|
||||||
|
|
||||||
|
var type;
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var range = ranges[i], cur = range.head, curType;
|
||||||
|
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
||||||
|
if (opening && !range.empty()) {
|
||||||
|
curType = "surround";
|
||||||
|
} else if ((identical || !opening) && next == ch) {
|
||||||
|
if (identical && stringStartsAfter(cm, cur))
|
||||||
|
curType = "both";
|
||||||
|
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
|
||||||
|
curType = "skipThree";
|
||||||
|
else
|
||||||
|
curType = "skip";
|
||||||
|
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
|
||||||
|
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
|
||||||
|
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
|
||||||
|
curType = "addFour";
|
||||||
|
} else if (identical) {
|
||||||
|
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
|
||||||
|
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
|
||||||
|
else return CodeMirror.Pass;
|
||||||
|
} else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
|
||||||
|
curType = "both";
|
||||||
|
} else {
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
}
|
||||||
|
if (!type) type = curType;
|
||||||
|
else if (type != curType) return CodeMirror.Pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
|
||||||
|
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
|
||||||
|
cm.operation(function() {
|
||||||
|
if (type == "skip") {
|
||||||
|
cm.execCommand("goCharRight");
|
||||||
|
} else if (type == "skipThree") {
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
cm.execCommand("goCharRight");
|
||||||
|
} else if (type == "surround") {
|
||||||
|
var sels = cm.getSelections();
|
||||||
|
for (var i = 0; i < sels.length; i++)
|
||||||
|
sels[i] = left + sels[i] + right;
|
||||||
|
cm.replaceSelections(sels, "around");
|
||||||
|
sels = cm.listSelections().slice();
|
||||||
|
for (var i = 0; i < sels.length; i++)
|
||||||
|
sels[i] = contractSelection(sels[i]);
|
||||||
|
cm.setSelections(sels);
|
||||||
|
} else if (type == "both") {
|
||||||
|
cm.replaceSelection(left + right, null);
|
||||||
|
cm.triggerElectric(left + right);
|
||||||
|
cm.execCommand("goCharLeft");
|
||||||
|
} else if (type == "addFour") {
|
||||||
|
cm.replaceSelection(left + left + left + left, "before");
|
||||||
|
cm.execCommand("goCharRight");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function charsAround(cm, pos) {
|
||||||
|
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
|
||||||
|
Pos(pos.line, pos.ch + 1));
|
||||||
|
return str.length == 2 ? str : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringStartsAfter(cm, pos) {
|
||||||
|
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
|
||||||
|
return /\bstring/.test(token.type) && token.start == pos.ch &&
|
||||||
|
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
|
||||||
|
}
|
||||||
|
});
|
||||||
185
src/modules/_CodeMirror/addon/edit/closetag.js
Normal file
185
src/modules/_CodeMirror/addon/edit/closetag.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag-closer extension for CodeMirror.
|
||||||
|
*
|
||||||
|
* This extension adds an "autoCloseTags" option that can be set to
|
||||||
|
* either true to get the default behavior, or an object to further
|
||||||
|
* configure its behavior.
|
||||||
|
*
|
||||||
|
* These are supported options:
|
||||||
|
*
|
||||||
|
* `whenClosing` (default true)
|
||||||
|
* Whether to autoclose when the '/' of a closing tag is typed.
|
||||||
|
* `whenOpening` (default true)
|
||||||
|
* Whether to autoclose the tag when the final '>' of an opening
|
||||||
|
* tag is typed.
|
||||||
|
* `dontCloseTags` (default is empty tags for HTML, none for XML)
|
||||||
|
* An array of tag names that should not be autoclosed.
|
||||||
|
* `indentTags` (default is block tags for HTML, none for XML)
|
||||||
|
* An array of tag names that should, when opened, cause a
|
||||||
|
* blank line to be added inside the tag, and the blank line and
|
||||||
|
* closing line to be indented.
|
||||||
|
* `emptyTags` (default is none)
|
||||||
|
* An array of XML tag names that should be autoclosed with '/>'.
|
||||||
|
*
|
||||||
|
* See demos/closetag.html for a usage example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
|
||||||
|
if (old != CodeMirror.Init && old)
|
||||||
|
cm.removeKeyMap("autoCloseTags");
|
||||||
|
if (!val) return;
|
||||||
|
var map = {name: "autoCloseTags"};
|
||||||
|
if (typeof val != "object" || val.whenClosing !== false)
|
||||||
|
map["'/'"] = function(cm) { return autoCloseSlash(cm); };
|
||||||
|
if (typeof val != "object" || val.whenOpening !== false)
|
||||||
|
map["'>'"] = function(cm) { return autoCloseGT(cm); };
|
||||||
|
cm.addKeyMap(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
|
||||||
|
"source", "track", "wbr"];
|
||||||
|
var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
|
||||||
|
"h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
|
||||||
|
|
||||||
|
function autoCloseGT(cm) {
|
||||||
|
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
var ranges = cm.listSelections(), replacements = [];
|
||||||
|
var opt = cm.getOption("autoCloseTags");
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||||
|
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
|
||||||
|
var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
|
||||||
|
var tagName = tagInfo && tagInfo.name
|
||||||
|
if (!tagName) return CodeMirror.Pass
|
||||||
|
|
||||||
|
var html = inner.mode.configuration == "html";
|
||||||
|
var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
|
||||||
|
var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
|
||||||
|
|
||||||
|
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
|
||||||
|
var lowerTagName = tagName.toLowerCase();
|
||||||
|
// Don't process the '>' at the end of an end-tag or self-closing tag
|
||||||
|
if (!tagName ||
|
||||||
|
tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
|
||||||
|
tok.type == "tag" && tagInfo.close ||
|
||||||
|
tok.string.indexOf("/") == (pos.ch - tok.start - 1) || // match something like <someTagName />
|
||||||
|
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
|
||||||
|
closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var emptyTags = typeof opt == "object" && opt.emptyTags;
|
||||||
|
if (emptyTags && indexOf(emptyTags, tagName) > -1) {
|
||||||
|
replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
|
||||||
|
replacements[i] = {indent: indent,
|
||||||
|
text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
|
||||||
|
newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
|
||||||
|
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||||
|
var info = replacements[i];
|
||||||
|
cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
|
||||||
|
var sel = cm.listSelections().slice(0);
|
||||||
|
sel[i] = {head: info.newPos, anchor: info.newPos};
|
||||||
|
cm.setSelections(sel);
|
||||||
|
if (!dontIndentOnAutoClose && info.indent) {
|
||||||
|
cm.indentLine(info.newPos.line, null, true);
|
||||||
|
cm.indentLine(info.newPos.line + 1, null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoCloseCurrent(cm, typingSlash) {
|
||||||
|
var ranges = cm.listSelections(), replacements = [];
|
||||||
|
var head = typingSlash ? "/" : "</";
|
||||||
|
var opt = cm.getOption("autoCloseTags");
|
||||||
|
var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||||
|
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
|
||||||
|
if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
|
||||||
|
tok.start != pos.ch - 1))
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
// Kludge to get around the fact that we are not in XML mode
|
||||||
|
// when completing in JS/CSS snippet in htmlmixed mode. Does not
|
||||||
|
// work for other XML embedded languages (there is no general
|
||||||
|
// way to go from a mixed mode to its current XML state).
|
||||||
|
var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
|
||||||
|
if (mixed && inner.mode.name == "javascript") {
|
||||||
|
replacement = head + "script";
|
||||||
|
} else if (mixed && inner.mode.name == "css") {
|
||||||
|
replacement = head + "style";
|
||||||
|
} else {
|
||||||
|
var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
|
||||||
|
var top = context.length ? context[context.length - 1] : ""
|
||||||
|
if (!context || (context.length && closingTagExists(cm, context, top, pos)))
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
replacement = head + top
|
||||||
|
}
|
||||||
|
if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
|
||||||
|
replacements[i] = replacement;
|
||||||
|
}
|
||||||
|
cm.replaceSelections(replacements);
|
||||||
|
ranges = cm.listSelections();
|
||||||
|
if (!dontIndentOnAutoClose) {
|
||||||
|
for (var i = 0; i < ranges.length; i++)
|
||||||
|
if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
|
||||||
|
cm.indentLine(ranges[i].head.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoCloseSlash(cm) {
|
||||||
|
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
return autoCloseCurrent(cm, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
|
||||||
|
|
||||||
|
function indexOf(collection, elt) {
|
||||||
|
if (collection.indexOf) return collection.indexOf(elt);
|
||||||
|
for (var i = 0, e = collection.length; i < e; ++i)
|
||||||
|
if (collection[i] == elt) return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If xml-fold is loaded, we use its functionality to try and verify
|
||||||
|
// whether a given tag is actually unclosed.
|
||||||
|
function closingTagExists(cm, context, tagName, pos, newTag) {
|
||||||
|
if (!CodeMirror.scanForClosingTag) return false;
|
||||||
|
var end = Math.min(cm.lastLine() + 1, pos.line + 500);
|
||||||
|
var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
|
||||||
|
if (!nextClose || nextClose.tag != tagName) return false;
|
||||||
|
// If the immediate wrapping context contains onCx instances of
|
||||||
|
// the same tag, a closing tag only exists if there are at least
|
||||||
|
// that many closing tags of that type following.
|
||||||
|
var onCx = newTag ? 1 : 0
|
||||||
|
for (var i = context.length - 1; i >= 0; i--) {
|
||||||
|
if (context[i] == tagName) ++onCx
|
||||||
|
else break
|
||||||
|
}
|
||||||
|
pos = nextClose.to;
|
||||||
|
for (var i = 1; i < onCx; i++) {
|
||||||
|
var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
|
||||||
|
if (!next || next.tag != tagName) return false;
|
||||||
|
pos = next.to;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
101
src/modules/_CodeMirror/addon/edit/continuelist.js
Normal file
101
src/modules/_CodeMirror/addon/edit/continuelist.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
|
||||||
|
emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
|
||||||
|
unorderedListRE = /[*+-]\s/;
|
||||||
|
|
||||||
|
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
|
||||||
|
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||||
|
var ranges = cm.listSelections(), replacements = [];
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var pos = ranges[i].head;
|
||||||
|
|
||||||
|
// If we're not in Markdown mode, fall back to normal newlineAndIndent
|
||||||
|
var eolState = cm.getStateAfter(pos.line);
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), eolState);
|
||||||
|
if (inner.mode.name !== "markdown") {
|
||||||
|
cm.execCommand("newlineAndIndent");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
eolState = inner.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inList = eolState.list !== false;
|
||||||
|
var inQuote = eolState.quote !== 0;
|
||||||
|
|
||||||
|
var line = cm.getLine(pos.line), match = listRE.exec(line);
|
||||||
|
var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
|
||||||
|
if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
|
||||||
|
cm.execCommand("newlineAndIndent");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (emptyListRE.test(line)) {
|
||||||
|
var endOfQuote = inQuote && />\s*$/.test(line)
|
||||||
|
var endOfList = !/>\s*$/.test(line)
|
||||||
|
if (endOfQuote || endOfList) cm.replaceRange("", {
|
||||||
|
line: pos.line, ch: 0
|
||||||
|
}, {
|
||||||
|
line: pos.line, ch: pos.ch + 1
|
||||||
|
});
|
||||||
|
replacements[i] = "\n";
|
||||||
|
} else {
|
||||||
|
var indent = match[1], after = match[5];
|
||||||
|
var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
|
||||||
|
var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
|
||||||
|
replacements[i] = "\n" + indent + bullet + after;
|
||||||
|
|
||||||
|
if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.replaceSelections(replacements);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Auto-updating Markdown list numbers when a new item is added to the
|
||||||
|
// middle of a list
|
||||||
|
function incrementRemainingMarkdownListNumbers(cm, pos) {
|
||||||
|
var startLine = pos.line, lookAhead = 0, skipCount = 0;
|
||||||
|
var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
|
||||||
|
|
||||||
|
do {
|
||||||
|
lookAhead += 1;
|
||||||
|
var nextLineNumber = startLine + lookAhead;
|
||||||
|
var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
|
||||||
|
|
||||||
|
if (nextItem) {
|
||||||
|
var nextIndent = nextItem[1];
|
||||||
|
var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
|
||||||
|
var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
|
||||||
|
|
||||||
|
if (startIndent === nextIndent && !isNaN(nextNumber)) {
|
||||||
|
if (newNumber === nextNumber) itemNumber = nextNumber + 1;
|
||||||
|
if (newNumber > nextNumber) itemNumber = newNumber + 1;
|
||||||
|
cm.replaceRange(
|
||||||
|
nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
|
||||||
|
{
|
||||||
|
line: nextLineNumber, ch: 0
|
||||||
|
}, {
|
||||||
|
line: nextLineNumber, ch: nextLine.length
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (startIndent.length > nextIndent.length) return;
|
||||||
|
// This doesn't run if the next line immediatley indents, as it is
|
||||||
|
// not clear of the users intention (new indented item or same level)
|
||||||
|
if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
|
||||||
|
skipCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (nextItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
158
src/modules/_CodeMirror/addon/edit/matchbrackets.js
Normal file
158
src/modules/_CodeMirror/addon/edit/matchbrackets.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
||||||
|
(document.documentMode == null || document.documentMode < 8);
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
|
||||||
|
|
||||||
|
function bracketRegex(config) {
|
||||||
|
return config && config.bracketRegex || /[(){}[\]]/
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMatchingBracket(cm, where, config) {
|
||||||
|
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
|
||||||
|
var afterCursor = config && config.afterCursor
|
||||||
|
if (afterCursor == null)
|
||||||
|
afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
|
||||||
|
var re = bracketRegex(config)
|
||||||
|
|
||||||
|
// A cursor is defined as between two characters, but in in vim command mode
|
||||||
|
// (i.e. not insert mode), the cursor is visually represented as a
|
||||||
|
// highlighted box on top of the 2nd character. Otherwise, we allow matches
|
||||||
|
// from before or after the cursor.
|
||||||
|
var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
|
||||||
|
re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
|
||||||
|
if (!match) return null;
|
||||||
|
var dir = match.charAt(1) == ">" ? 1 : -1;
|
||||||
|
if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
|
||||||
|
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
|
||||||
|
|
||||||
|
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
|
||||||
|
if (found == null) return null;
|
||||||
|
return {from: Pos(where.line, pos), to: found && found.pos,
|
||||||
|
match: found && found.ch == match.charAt(0), forward: dir > 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
// bracketRegex is used to specify which type of bracket to scan
|
||||||
|
// should be a regexp, e.g. /[[\]]/
|
||||||
|
//
|
||||||
|
// Note: If "where" is on an open bracket, then this bracket is ignored.
|
||||||
|
//
|
||||||
|
// Returns false when no bracket was found, null when it reached
|
||||||
|
// maxScanLines and gave up
|
||||||
|
function scanForBracket(cm, where, dir, style, config) {
|
||||||
|
var maxScanLen = (config && config.maxScanLineLength) || 10000;
|
||||||
|
var maxScanLines = (config && config.maxScanLines) || 1000;
|
||||||
|
|
||||||
|
var stack = [];
|
||||||
|
var re = bracketRegex(config)
|
||||||
|
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
|
||||||
|
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
||||||
|
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
||||||
|
var line = cm.getLine(lineNo);
|
||||||
|
if (!line) continue;
|
||||||
|
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
||||||
|
if (line.length > maxScanLen) continue;
|
||||||
|
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
|
||||||
|
for (; pos != end; pos += dir) {
|
||||||
|
var ch = line.charAt(pos);
|
||||||
|
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
|
||||||
|
var match = matching[ch];
|
||||||
|
if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
|
||||||
|
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
|
||||||
|
else stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchBrackets(cm, autoclear, config) {
|
||||||
|
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
||||||
|
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
|
||||||
|
var marks = [], ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
|
||||||
|
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
|
||||||
|
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
||||||
|
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
|
||||||
|
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
|
||||||
|
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (marks.length) {
|
||||||
|
// Kludge to work around the IE bug from issue #1193, where text
|
||||||
|
// input stops going to the textare whever this fires.
|
||||||
|
if (ie_lt8 && cm.state.focused) cm.focus();
|
||||||
|
|
||||||
|
var clear = function() {
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (autoclear) setTimeout(clear, 800);
|
||||||
|
else return clear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doMatchBrackets(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
if (cm.state.matchBrackets.currentlyHighlighted) {
|
||||||
|
cm.state.matchBrackets.currentlyHighlighted();
|
||||||
|
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||||
|
}
|
||||||
|
cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
||||||
|
function clear(cm) {
|
||||||
|
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
|
||||||
|
cm.state.matchBrackets.currentlyHighlighted();
|
||||||
|
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
cm.off("cursorActivity", doMatchBrackets);
|
||||||
|
cm.off("focus", doMatchBrackets)
|
||||||
|
cm.off("blur", clear)
|
||||||
|
clear(cm);
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
||||||
|
cm.on("cursorActivity", doMatchBrackets);
|
||||||
|
cm.on("focus", doMatchBrackets)
|
||||||
|
cm.on("blur", clear)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
||||||
|
CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
|
||||||
|
// Backwards-compatibility kludge
|
||||||
|
if (oldConfig || typeof config == "boolean") {
|
||||||
|
if (!oldConfig) {
|
||||||
|
config = config ? {strict: true} : null
|
||||||
|
} else {
|
||||||
|
oldConfig.strict = config
|
||||||
|
config = oldConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findMatchingBracket(this, pos, config)
|
||||||
|
});
|
||||||
|
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
|
||||||
|
return scanForBracket(this, pos, dir, style, config);
|
||||||
|
});
|
||||||
|
});
|
||||||
66
src/modules/_CodeMirror/addon/edit/matchtags.js
Normal file
66
src/modules/_CodeMirror/addon/edit/matchtags.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
cm.off("cursorActivity", doMatchTags);
|
||||||
|
cm.off("viewportChange", maybeUpdateMatch);
|
||||||
|
clear(cm);
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.state.matchBothTags = typeof val == "object" && val.bothTags;
|
||||||
|
cm.on("cursorActivity", doMatchTags);
|
||||||
|
cm.on("viewportChange", maybeUpdateMatch);
|
||||||
|
doMatchTags(cm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function clear(cm) {
|
||||||
|
if (cm.state.tagHit) cm.state.tagHit.clear();
|
||||||
|
if (cm.state.tagOther) cm.state.tagOther.clear();
|
||||||
|
cm.state.tagHit = cm.state.tagOther = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doMatchTags(cm) {
|
||||||
|
cm.state.failedTagMatch = false;
|
||||||
|
cm.operation(function() {
|
||||||
|
clear(cm);
|
||||||
|
if (cm.somethingSelected()) return;
|
||||||
|
var cur = cm.getCursor(), range = cm.getViewport();
|
||||||
|
range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
|
||||||
|
var match = CodeMirror.findMatchingTag(cm, cur, range);
|
||||||
|
if (!match) return;
|
||||||
|
if (cm.state.matchBothTags) {
|
||||||
|
var hit = match.at == "open" ? match.open : match.close;
|
||||||
|
if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
|
||||||
|
}
|
||||||
|
var other = match.at == "close" ? match.open : match.close;
|
||||||
|
if (other)
|
||||||
|
cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
|
||||||
|
else
|
||||||
|
cm.state.failedTagMatch = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeUpdateMatch(cm) {
|
||||||
|
if (cm.state.failedTagMatch) doMatchTags(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.commands.toMatchingTag = function(cm) {
|
||||||
|
var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
|
||||||
|
if (found) {
|
||||||
|
var other = found.at == "close" ? found.open : found.close;
|
||||||
|
if (other) cm.extendSelection(other.to, other.from);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
27
src/modules/_CodeMirror/addon/edit/trailingspace.js
Normal file
27
src/modules/_CodeMirror/addon/edit/trailingspace.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
|
||||||
|
if (prev == CodeMirror.Init) prev = false;
|
||||||
|
if (prev && !val)
|
||||||
|
cm.removeOverlay("trailingspace");
|
||||||
|
else if (!prev && val)
|
||||||
|
cm.addOverlay({
|
||||||
|
token: function(stream) {
|
||||||
|
for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
|
||||||
|
if (i > stream.pos) { stream.pos = i; return null; }
|
||||||
|
stream.pos = l;
|
||||||
|
return "trailingspace";
|
||||||
|
},
|
||||||
|
name: "trailingspace"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
105
src/modules/_CodeMirror/addon/fold/brace-fold.js
Normal file
105
src/modules/_CodeMirror/addon/fold/brace-fold.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "brace", function(cm, start) {
|
||||||
|
var line = start.line, lineText = cm.getLine(line);
|
||||||
|
var tokenType;
|
||||||
|
|
||||||
|
function findOpening(openCh) {
|
||||||
|
for (var at = start.ch, pass = 0;;) {
|
||||||
|
var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
|
||||||
|
if (found == -1) {
|
||||||
|
if (pass == 1) break;
|
||||||
|
pass = 1;
|
||||||
|
at = lineText.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pass == 1 && found < start.ch) break;
|
||||||
|
tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
|
||||||
|
if (!/^(comment|string)/.test(tokenType)) return found + 1;
|
||||||
|
at = found - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var startToken = "{", endToken = "}", startCh = findOpening("{");
|
||||||
|
if (startCh == null) {
|
||||||
|
startToken = "[", endToken = "]";
|
||||||
|
startCh = findOpening("[");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startCh == null) return;
|
||||||
|
var count = 1, lastLine = cm.lastLine(), end, endCh;
|
||||||
|
outer: for (var i = line; i <= lastLine; ++i) {
|
||||||
|
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
||||||
|
for (;;) {
|
||||||
|
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
|
||||||
|
if (nextOpen < 0) nextOpen = text.length;
|
||||||
|
if (nextClose < 0) nextClose = text.length;
|
||||||
|
pos = Math.min(nextOpen, nextClose);
|
||||||
|
if (pos == text.length) break;
|
||||||
|
if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
|
||||||
|
if (pos == nextOpen) ++count;
|
||||||
|
else if (!--count) { end = i; endCh = pos; break outer; }
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (end == null || line == end) return;
|
||||||
|
return {from: CodeMirror.Pos(line, startCh),
|
||||||
|
to: CodeMirror.Pos(end, endCh)};
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "import", function(cm, start) {
|
||||||
|
function hasImport(line) {
|
||||||
|
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
||||||
|
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
||||||
|
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
||||||
|
if (start.type != "keyword" || start.string != "import") return null;
|
||||||
|
// Now find closing semicolon, return its position
|
||||||
|
for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
|
||||||
|
var text = cm.getLine(i), semi = text.indexOf(";");
|
||||||
|
if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var startLine = start.line, has = hasImport(startLine), prev;
|
||||||
|
if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
|
||||||
|
return null;
|
||||||
|
for (var end = has.end;;) {
|
||||||
|
var next = hasImport(end.line + 1);
|
||||||
|
if (next == null) break;
|
||||||
|
end = next.end;
|
||||||
|
}
|
||||||
|
return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "include", function(cm, start) {
|
||||||
|
function hasInclude(line) {
|
||||||
|
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
||||||
|
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
||||||
|
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
||||||
|
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startLine = start.line, has = hasInclude(startLine);
|
||||||
|
if (has == null || hasInclude(startLine - 1) != null) return null;
|
||||||
|
for (var end = startLine;;) {
|
||||||
|
var next = hasInclude(end + 1);
|
||||||
|
if (next == null) break;
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
return {from: CodeMirror.Pos(startLine, has + 1),
|
||||||
|
to: cm.clipPos(CodeMirror.Pos(end))};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
59
src/modules/_CodeMirror/addon/fold/comment-fold.js
Normal file
59
src/modules/_CodeMirror/addon/fold/comment-fold.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
|
||||||
|
return mode.blockCommentStart && mode.blockCommentEnd;
|
||||||
|
}, function(cm, start) {
|
||||||
|
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
|
||||||
|
if (!startToken || !endToken) return;
|
||||||
|
var line = start.line, lineText = cm.getLine(line);
|
||||||
|
|
||||||
|
var startCh;
|
||||||
|
for (var at = start.ch, pass = 0;;) {
|
||||||
|
var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
|
||||||
|
if (found == -1) {
|
||||||
|
if (pass == 1) return;
|
||||||
|
pass = 1;
|
||||||
|
at = lineText.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pass == 1 && found < start.ch) return;
|
||||||
|
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
|
||||||
|
(found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
|
||||||
|
!/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
|
||||||
|
startCh = found + startToken.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
at = found - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var depth = 1, lastLine = cm.lastLine(), end, endCh;
|
||||||
|
outer: for (var i = line; i <= lastLine; ++i) {
|
||||||
|
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
||||||
|
for (;;) {
|
||||||
|
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
|
||||||
|
if (nextOpen < 0) nextOpen = text.length;
|
||||||
|
if (nextClose < 0) nextClose = text.length;
|
||||||
|
pos = Math.min(nextOpen, nextClose);
|
||||||
|
if (pos == text.length) break;
|
||||||
|
if (pos == nextOpen) ++depth;
|
||||||
|
else if (!--depth) { end = i; endCh = pos; break outer; }
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (end == null || line == end && endCh == startCh) return;
|
||||||
|
return {from: CodeMirror.Pos(line, startCh),
|
||||||
|
to: CodeMirror.Pos(end, endCh)};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
157
src/modules/_CodeMirror/addon/fold/foldcode.js
Normal file
157
src/modules/_CodeMirror/addon/fold/foldcode.js
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function doFold(cm, pos, options, force) {
|
||||||
|
if (options && options.call) {
|
||||||
|
var finder = options;
|
||||||
|
options = null;
|
||||||
|
} else {
|
||||||
|
var finder = getOption(cm, options, "rangeFinder");
|
||||||
|
}
|
||||||
|
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
|
||||||
|
var minSize = getOption(cm, options, "minFoldSize");
|
||||||
|
|
||||||
|
function getRange(allowFolded) {
|
||||||
|
var range = finder(cm, pos);
|
||||||
|
if (!range || range.to.line - range.from.line < minSize) return null;
|
||||||
|
var marks = cm.findMarksAt(range.from);
|
||||||
|
for (var i = 0; i < marks.length; ++i) {
|
||||||
|
if (marks[i].__isFold && force !== "fold") {
|
||||||
|
if (!allowFolded) return null;
|
||||||
|
range.cleared = true;
|
||||||
|
marks[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
var range = getRange(true);
|
||||||
|
if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
|
||||||
|
pos = CodeMirror.Pos(pos.line - 1, 0);
|
||||||
|
range = getRange(false);
|
||||||
|
}
|
||||||
|
if (!range || range.cleared || force === "unfold") return;
|
||||||
|
|
||||||
|
var myWidget = makeWidget(cm, options, range);
|
||||||
|
CodeMirror.on(myWidget, "mousedown", function(e) {
|
||||||
|
myRange.clear();
|
||||||
|
CodeMirror.e_preventDefault(e);
|
||||||
|
});
|
||||||
|
var myRange = cm.markText(range.from, range.to, {
|
||||||
|
replacedWith: myWidget,
|
||||||
|
clearOnEnter: getOption(cm, options, "clearOnEnter"),
|
||||||
|
__isFold: true
|
||||||
|
});
|
||||||
|
myRange.on("clear", function(from, to) {
|
||||||
|
CodeMirror.signal(cm, "unfold", cm, from, to);
|
||||||
|
});
|
||||||
|
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeWidget(cm, options, range) {
|
||||||
|
var widget = getOption(cm, options, "widget");
|
||||||
|
|
||||||
|
if (typeof widget == "function") {
|
||||||
|
widget = widget(range.from, range.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof widget == "string") {
|
||||||
|
var text = document.createTextNode(widget);
|
||||||
|
widget = document.createElement("span");
|
||||||
|
widget.appendChild(text);
|
||||||
|
widget.className = "CodeMirror-foldmarker";
|
||||||
|
} else if (widget) {
|
||||||
|
widget = widget.cloneNode(true)
|
||||||
|
}
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clumsy backwards-compatible interface
|
||||||
|
CodeMirror.newFoldFunction = function(rangeFinder, widget) {
|
||||||
|
return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// New-style interface
|
||||||
|
CodeMirror.defineExtension("foldCode", function(pos, options, force) {
|
||||||
|
doFold(this, pos, options, force);
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("isFolded", function(pos) {
|
||||||
|
var marks = this.findMarksAt(pos);
|
||||||
|
for (var i = 0; i < marks.length; ++i)
|
||||||
|
if (marks[i].__isFold) return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.commands.toggleFold = function(cm) {
|
||||||
|
cm.foldCode(cm.getCursor());
|
||||||
|
};
|
||||||
|
CodeMirror.commands.fold = function(cm) {
|
||||||
|
cm.foldCode(cm.getCursor(), null, "fold");
|
||||||
|
};
|
||||||
|
CodeMirror.commands.unfold = function(cm) {
|
||||||
|
cm.foldCode(cm.getCursor(), null, "unfold");
|
||||||
|
};
|
||||||
|
CodeMirror.commands.foldAll = function(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
|
||||||
|
cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
CodeMirror.commands.unfoldAll = function(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
|
||||||
|
cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "combine", function() {
|
||||||
|
var funcs = Array.prototype.slice.call(arguments, 0);
|
||||||
|
return function(cm, start) {
|
||||||
|
for (var i = 0; i < funcs.length; ++i) {
|
||||||
|
var found = funcs[i](cm, start);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "auto", function(cm, start) {
|
||||||
|
var helpers = cm.getHelpers(start, "fold");
|
||||||
|
for (var i = 0; i < helpers.length; i++) {
|
||||||
|
var cur = helpers[i](cm, start);
|
||||||
|
if (cur) return cur;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var defaultOptions = {
|
||||||
|
rangeFinder: CodeMirror.fold.auto,
|
||||||
|
widget: "\u2194",
|
||||||
|
minFoldSize: 0,
|
||||||
|
scanUp: false,
|
||||||
|
clearOnEnter: true
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.defineOption("foldOptions", null);
|
||||||
|
|
||||||
|
function getOption(cm, options, name) {
|
||||||
|
if (options && options[name] !== undefined)
|
||||||
|
return options[name];
|
||||||
|
var editorOptions = cm.options.foldOptions;
|
||||||
|
if (editorOptions && editorOptions[name] !== undefined)
|
||||||
|
return editorOptions[name];
|
||||||
|
return defaultOptions[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("foldOption", function(options, name) {
|
||||||
|
return getOption(this, options, name);
|
||||||
|
});
|
||||||
|
});
|
||||||
20
src/modules/_CodeMirror/addon/fold/foldgutter.css
Normal file
20
src/modules/_CodeMirror/addon/fold/foldgutter.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.CodeMirror-foldmarker {
|
||||||
|
color: blue;
|
||||||
|
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
|
||||||
|
font-family: arial;
|
||||||
|
line-height: .3;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.CodeMirror-foldgutter {
|
||||||
|
width: .7em;
|
||||||
|
}
|
||||||
|
.CodeMirror-foldgutter-open,
|
||||||
|
.CodeMirror-foldgutter-folded {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.CodeMirror-foldgutter-open:after {
|
||||||
|
content: "\25BE";
|
||||||
|
}
|
||||||
|
.CodeMirror-foldgutter-folded:after {
|
||||||
|
content: "\25B8";
|
||||||
|
}
|
||||||
163
src/modules/_CodeMirror/addon/fold/foldgutter.js
Normal file
163
src/modules/_CodeMirror/addon/fold/foldgutter.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./foldcode"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./foldcode"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
cm.clearGutter(cm.state.foldGutter.options.gutter);
|
||||||
|
cm.state.foldGutter = null;
|
||||||
|
cm.off("gutterClick", onGutterClick);
|
||||||
|
cm.off("changes", onChange);
|
||||||
|
cm.off("viewportChange", onViewportChange);
|
||||||
|
cm.off("fold", onFold);
|
||||||
|
cm.off("unfold", onFold);
|
||||||
|
cm.off("swapDoc", onChange);
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.state.foldGutter = new State(parseOptions(val));
|
||||||
|
updateInViewport(cm);
|
||||||
|
cm.on("gutterClick", onGutterClick);
|
||||||
|
cm.on("changes", onChange);
|
||||||
|
cm.on("viewportChange", onViewportChange);
|
||||||
|
cm.on("fold", onFold);
|
||||||
|
cm.on("unfold", onFold);
|
||||||
|
cm.on("swapDoc", onChange);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
function State(options) {
|
||||||
|
this.options = options;
|
||||||
|
this.from = this.to = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOptions(opts) {
|
||||||
|
if (opts === true) opts = {};
|
||||||
|
if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
|
||||||
|
if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
|
||||||
|
if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFolded(cm, line) {
|
||||||
|
var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
|
||||||
|
for (var i = 0; i < marks.length; ++i) {
|
||||||
|
if (marks[i].__isFold) {
|
||||||
|
var fromPos = marks[i].find(-1);
|
||||||
|
if (fromPos && fromPos.line === line)
|
||||||
|
return marks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function marker(spec) {
|
||||||
|
if (typeof spec == "string") {
|
||||||
|
var elt = document.createElement("div");
|
||||||
|
elt.className = spec + " CodeMirror-guttermarker-subtle";
|
||||||
|
return elt;
|
||||||
|
} else {
|
||||||
|
return spec.cloneNode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFoldInfo(cm, from, to) {
|
||||||
|
var opts = cm.state.foldGutter.options, cur = from - 1;
|
||||||
|
var minSize = cm.foldOption(opts, "minFoldSize");
|
||||||
|
var func = cm.foldOption(opts, "rangeFinder");
|
||||||
|
// we can reuse the built-in indicator element if its className matches the new state
|
||||||
|
var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded);
|
||||||
|
var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen);
|
||||||
|
cm.eachLine(from, to, function(line) {
|
||||||
|
++cur;
|
||||||
|
var mark = null;
|
||||||
|
var old = line.gutterMarkers;
|
||||||
|
if (old) old = old[opts.gutter];
|
||||||
|
if (isFolded(cm, cur)) {
|
||||||
|
if (clsFolded && old && clsFolded.test(old.className)) return;
|
||||||
|
mark = marker(opts.indicatorFolded);
|
||||||
|
} else {
|
||||||
|
var pos = Pos(cur, 0);
|
||||||
|
var range = func && func(cm, pos);
|
||||||
|
if (range && range.to.line - range.from.line >= minSize) {
|
||||||
|
if (clsOpen && old && clsOpen.test(old.className)) return;
|
||||||
|
mark = marker(opts.indicatorOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!mark && !old) return;
|
||||||
|
cm.setGutterMarker(line, opts.gutter, mark);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from CodeMirror/src/util/dom.js
|
||||||
|
function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
|
||||||
|
|
||||||
|
function updateInViewport(cm) {
|
||||||
|
var vp = cm.getViewport(), state = cm.state.foldGutter;
|
||||||
|
if (!state) return;
|
||||||
|
cm.operation(function() {
|
||||||
|
updateFoldInfo(cm, vp.from, vp.to);
|
||||||
|
});
|
||||||
|
state.from = vp.from; state.to = vp.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGutterClick(cm, line, gutter) {
|
||||||
|
var state = cm.state.foldGutter;
|
||||||
|
if (!state) return;
|
||||||
|
var opts = state.options;
|
||||||
|
if (gutter != opts.gutter) return;
|
||||||
|
var folded = isFolded(cm, line);
|
||||||
|
if (folded) folded.clear();
|
||||||
|
else cm.foldCode(Pos(line, 0), opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(cm) {
|
||||||
|
var state = cm.state.foldGutter;
|
||||||
|
if (!state) return;
|
||||||
|
var opts = state.options;
|
||||||
|
state.from = state.to = 0;
|
||||||
|
clearTimeout(state.changeUpdate);
|
||||||
|
state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onViewportChange(cm) {
|
||||||
|
var state = cm.state.foldGutter;
|
||||||
|
if (!state) return;
|
||||||
|
var opts = state.options;
|
||||||
|
clearTimeout(state.changeUpdate);
|
||||||
|
state.changeUpdate = setTimeout(function() {
|
||||||
|
var vp = cm.getViewport();
|
||||||
|
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
|
||||||
|
updateInViewport(cm);
|
||||||
|
} else {
|
||||||
|
cm.operation(function() {
|
||||||
|
if (vp.from < state.from) {
|
||||||
|
updateFoldInfo(cm, vp.from, state.from);
|
||||||
|
state.from = vp.from;
|
||||||
|
}
|
||||||
|
if (vp.to > state.to) {
|
||||||
|
updateFoldInfo(cm, state.to, vp.to);
|
||||||
|
state.to = vp.to;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, opts.updateViewportTimeSpan || 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFold(cm, from) {
|
||||||
|
var state = cm.state.foldGutter;
|
||||||
|
if (!state) return;
|
||||||
|
var line = from.line;
|
||||||
|
if (line >= state.from && line < state.to)
|
||||||
|
updateFoldInfo(cm, line, line + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
48
src/modules/_CodeMirror/addon/fold/indent-fold.js
Normal file
48
src/modules/_CodeMirror/addon/fold/indent-fold.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function lineIndent(cm, lineNo) {
|
||||||
|
var text = cm.getLine(lineNo)
|
||||||
|
var spaceTo = text.search(/\S/)
|
||||||
|
if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
|
||||||
|
return -1
|
||||||
|
return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
|
||||||
|
var myIndent = lineIndent(cm, start.line)
|
||||||
|
if (myIndent < 0) return
|
||||||
|
var lastLineInFold = null
|
||||||
|
|
||||||
|
// Go through lines until we find a line that definitely doesn't belong in
|
||||||
|
// the block we're folding, or to the end.
|
||||||
|
for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
|
||||||
|
var indent = lineIndent(cm, i)
|
||||||
|
if (indent == -1) {
|
||||||
|
} else if (indent > myIndent) {
|
||||||
|
// Lines with a greater indent are considered part of the block.
|
||||||
|
lastLineInFold = i;
|
||||||
|
} else {
|
||||||
|
// If this line has non-space, non-comment content, and is
|
||||||
|
// indented less or equal to the start line, it is the start of
|
||||||
|
// another block.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastLineInFold) return {
|
||||||
|
from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
|
||||||
|
to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
49
src/modules/_CodeMirror/addon/fold/markdown-fold.js
Normal file
49
src/modules/_CodeMirror/addon/fold/markdown-fold.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
|
||||||
|
var maxDepth = 100;
|
||||||
|
|
||||||
|
function isHeader(lineNo) {
|
||||||
|
var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
|
||||||
|
return tokentype && /\bheader\b/.test(tokentype);
|
||||||
|
}
|
||||||
|
|
||||||
|
function headerLevel(lineNo, line, nextLine) {
|
||||||
|
var match = line && line.match(/^#+/);
|
||||||
|
if (match && isHeader(lineNo)) return match[0].length;
|
||||||
|
match = nextLine && nextLine.match(/^[=\-]+\s*$/);
|
||||||
|
if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
|
||||||
|
return maxDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
|
||||||
|
var level = headerLevel(start.line, firstLine, nextLine);
|
||||||
|
if (level === maxDepth) return undefined;
|
||||||
|
|
||||||
|
var lastLineNo = cm.lastLine();
|
||||||
|
var end = start.line, nextNextLine = cm.getLine(end + 2);
|
||||||
|
while (end < lastLineNo) {
|
||||||
|
if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
|
||||||
|
++end;
|
||||||
|
nextLine = nextNextLine;
|
||||||
|
nextNextLine = cm.getLine(end + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: CodeMirror.Pos(start.line, firstLine.length),
|
||||||
|
to: CodeMirror.Pos(end, cm.getLine(end).length)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
184
src/modules/_CodeMirror/addon/fold/xml-fold.js
Normal file
184
src/modules/_CodeMirror/addon/fold/xml-fold.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
|
||||||
|
|
||||||
|
var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
||||||
|
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
||||||
|
var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
|
||||||
|
|
||||||
|
function Iter(cm, line, ch, range) {
|
||||||
|
this.line = line; this.ch = ch;
|
||||||
|
this.cm = cm; this.text = cm.getLine(line);
|
||||||
|
this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
|
||||||
|
this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
function tagAt(iter, ch) {
|
||||||
|
var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
|
||||||
|
return type && /\btag\b/.test(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextLine(iter) {
|
||||||
|
if (iter.line >= iter.max) return;
|
||||||
|
iter.ch = 0;
|
||||||
|
iter.text = iter.cm.getLine(++iter.line);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function prevLine(iter) {
|
||||||
|
if (iter.line <= iter.min) return;
|
||||||
|
iter.text = iter.cm.getLine(--iter.line);
|
||||||
|
iter.ch = iter.text.length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toTagEnd(iter) {
|
||||||
|
for (;;) {
|
||||||
|
var gt = iter.text.indexOf(">", iter.ch);
|
||||||
|
if (gt == -1) { if (nextLine(iter)) continue; else return; }
|
||||||
|
if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
|
||||||
|
var lastSlash = iter.text.lastIndexOf("/", gt);
|
||||||
|
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
|
||||||
|
iter.ch = gt + 1;
|
||||||
|
return selfClose ? "selfClose" : "regular";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toTagStart(iter) {
|
||||||
|
for (;;) {
|
||||||
|
var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
|
||||||
|
if (lt == -1) { if (prevLine(iter)) continue; else return; }
|
||||||
|
if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
|
||||||
|
xmlTagStart.lastIndex = lt;
|
||||||
|
iter.ch = lt;
|
||||||
|
var match = xmlTagStart.exec(iter.text);
|
||||||
|
if (match && match.index == lt) return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNextTag(iter) {
|
||||||
|
for (;;) {
|
||||||
|
xmlTagStart.lastIndex = iter.ch;
|
||||||
|
var found = xmlTagStart.exec(iter.text);
|
||||||
|
if (!found) { if (nextLine(iter)) continue; else return; }
|
||||||
|
if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
|
||||||
|
iter.ch = found.index + found[0].length;
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toPrevTag(iter) {
|
||||||
|
for (;;) {
|
||||||
|
var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
|
||||||
|
if (gt == -1) { if (prevLine(iter)) continue; else return; }
|
||||||
|
if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
|
||||||
|
var lastSlash = iter.text.lastIndexOf("/", gt);
|
||||||
|
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
|
||||||
|
iter.ch = gt + 1;
|
||||||
|
return selfClose ? "selfClose" : "regular";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMatchingClose(iter, tag) {
|
||||||
|
var stack = [];
|
||||||
|
for (;;) {
|
||||||
|
var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
|
||||||
|
if (!next || !(end = toTagEnd(iter))) return;
|
||||||
|
if (end == "selfClose") continue;
|
||||||
|
if (next[1]) { // closing tag
|
||||||
|
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
|
||||||
|
stack.length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < 0 && (!tag || tag == next[2])) return {
|
||||||
|
tag: next[2],
|
||||||
|
from: Pos(startLine, startCh),
|
||||||
|
to: Pos(iter.line, iter.ch)
|
||||||
|
};
|
||||||
|
} else { // opening tag
|
||||||
|
stack.push(next[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function findMatchingOpen(iter, tag) {
|
||||||
|
var stack = [];
|
||||||
|
for (;;) {
|
||||||
|
var prev = toPrevTag(iter);
|
||||||
|
if (!prev) return;
|
||||||
|
if (prev == "selfClose") { toTagStart(iter); continue; }
|
||||||
|
var endLine = iter.line, endCh = iter.ch;
|
||||||
|
var start = toTagStart(iter);
|
||||||
|
if (!start) return;
|
||||||
|
if (start[1]) { // closing tag
|
||||||
|
stack.push(start[2]);
|
||||||
|
} else { // opening tag
|
||||||
|
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
|
||||||
|
stack.length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < 0 && (!tag || tag == start[2])) return {
|
||||||
|
tag: start[2],
|
||||||
|
from: Pos(iter.line, iter.ch),
|
||||||
|
to: Pos(endLine, endCh)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("fold", "xml", function(cm, start) {
|
||||||
|
var iter = new Iter(cm, start.line, 0);
|
||||||
|
for (;;) {
|
||||||
|
var openTag = toNextTag(iter)
|
||||||
|
if (!openTag || iter.line != start.line) return
|
||||||
|
var end = toTagEnd(iter)
|
||||||
|
if (!end) return
|
||||||
|
if (!openTag[1] && end != "selfClose") {
|
||||||
|
var startPos = Pos(iter.line, iter.ch);
|
||||||
|
var endPos = findMatchingClose(iter, openTag[2]);
|
||||||
|
return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CodeMirror.findMatchingTag = function(cm, pos, range) {
|
||||||
|
var iter = new Iter(cm, pos.line, pos.ch, range);
|
||||||
|
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
|
||||||
|
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
|
||||||
|
var start = end && toTagStart(iter);
|
||||||
|
if (!end || !start || cmp(iter, pos) > 0) return;
|
||||||
|
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
|
||||||
|
if (end == "selfClose") return {open: here, close: null, at: "open"};
|
||||||
|
|
||||||
|
if (start[1]) { // closing tag
|
||||||
|
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
|
||||||
|
} else { // opening tag
|
||||||
|
iter = new Iter(cm, to.line, to.ch, range);
|
||||||
|
return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
|
||||||
|
var iter = new Iter(cm, pos.line, pos.ch, range);
|
||||||
|
for (;;) {
|
||||||
|
var open = findMatchingOpen(iter, tag);
|
||||||
|
if (!open) break;
|
||||||
|
var forward = new Iter(cm, pos.line, pos.ch, range);
|
||||||
|
var close = findMatchingClose(forward, open.tag);
|
||||||
|
if (close) return {open: open, close: close};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used by addon/edit/closetag.js
|
||||||
|
CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
|
||||||
|
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
|
||||||
|
return findMatchingClose(iter, name);
|
||||||
|
};
|
||||||
|
});
|
||||||
41
src/modules/_CodeMirror/addon/hint/anyword-hint.js
Normal file
41
src/modules/_CodeMirror/addon/hint/anyword-hint.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var WORD = /[\w$]+/, RANGE = 500;
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
|
||||||
|
var word = options && options.word || WORD;
|
||||||
|
var range = options && options.range || RANGE;
|
||||||
|
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
|
||||||
|
var end = cur.ch, start = end;
|
||||||
|
while (start && word.test(curLine.charAt(start - 1))) --start;
|
||||||
|
var curWord = start != end && curLine.slice(start, end);
|
||||||
|
|
||||||
|
var list = options && options.list || [], seen = {};
|
||||||
|
var re = new RegExp(word.source, "g");
|
||||||
|
for (var dir = -1; dir <= 1; dir += 2) {
|
||||||
|
var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
|
||||||
|
for (; line != endLine; line += dir) {
|
||||||
|
var text = editor.getLine(line), m;
|
||||||
|
while (m = re.exec(text)) {
|
||||||
|
if (line == cur.line && m[0] === curWord) continue;
|
||||||
|
if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
|
||||||
|
seen[m[0]] = true;
|
||||||
|
list.push(m[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
|
||||||
|
});
|
||||||
|
});
|
||||||
66
src/modules/_CodeMirror/addon/hint/css-hint.js
Normal file
66
src/modules/_CodeMirror/addon/hint/css-hint.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("../../mode/css/css"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "../../mode/css/css"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var pseudoClasses = {"active":1, "after":1, "before":1, "checked":1, "default":1,
|
||||||
|
"disabled":1, "empty":1, "enabled":1, "first-child":1, "first-letter":1,
|
||||||
|
"first-line":1, "first-of-type":1, "focus":1, "hover":1, "in-range":1,
|
||||||
|
"indeterminate":1, "invalid":1, "lang":1, "last-child":1, "last-of-type":1,
|
||||||
|
"link":1, "not":1, "nth-child":1, "nth-last-child":1, "nth-last-of-type":1,
|
||||||
|
"nth-of-type":1, "only-of-type":1, "only-child":1, "optional":1, "out-of-range":1,
|
||||||
|
"placeholder":1, "read-only":1, "read-write":1, "required":1, "root":1,
|
||||||
|
"selection":1, "target":1, "valid":1, "visited":1
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "css", function(cm) {
|
||||||
|
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
||||||
|
if (inner.mode.name != "css") return;
|
||||||
|
|
||||||
|
if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
|
||||||
|
return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
|
||||||
|
to: CodeMirror.Pos(cur.line, token.end)};
|
||||||
|
|
||||||
|
var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
|
||||||
|
if (/[^\w$_-]/.test(word)) {
|
||||||
|
word = ""; start = end = cur.ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
var spec = CodeMirror.resolveMode("text/css");
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
function add(keywords) {
|
||||||
|
for (var name in keywords)
|
||||||
|
if (!word || name.lastIndexOf(word, 0) == 0)
|
||||||
|
result.push(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var st = inner.state.state;
|
||||||
|
if (st == "pseudo" || token.type == "variable-3") {
|
||||||
|
add(pseudoClasses);
|
||||||
|
} else if (st == "block" || st == "maybeprop") {
|
||||||
|
add(spec.propertyKeywords);
|
||||||
|
} else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
|
||||||
|
add(spec.valueKeywords);
|
||||||
|
add(spec.colorKeywords);
|
||||||
|
} else if (st == "media" || st == "media_parens") {
|
||||||
|
add(spec.mediaTypes);
|
||||||
|
add(spec.mediaFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length) return {
|
||||||
|
list: result,
|
||||||
|
from: CodeMirror.Pos(cur.line, start),
|
||||||
|
to: CodeMirror.Pos(cur.line, end)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
351
src/modules/_CodeMirror/addon/hint/html-hint.js
Normal file
351
src/modules/_CodeMirror/addon/hint/html-hint.js
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./xml-hint"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./xml-hint"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
|
||||||
|
var targets = ["_blank", "_self", "_top", "_parent"];
|
||||||
|
var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
|
||||||
|
var methods = ["get", "post", "put", "delete"];
|
||||||
|
var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
|
||||||
|
var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
|
||||||
|
"3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
|
||||||
|
"orientation:landscape", "device-height: [X]", "device-width: [X]"];
|
||||||
|
var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
a: {
|
||||||
|
attrs: {
|
||||||
|
href: null, ping: null, type: null,
|
||||||
|
media: media,
|
||||||
|
target: targets,
|
||||||
|
hreflang: langs
|
||||||
|
}
|
||||||
|
},
|
||||||
|
abbr: s,
|
||||||
|
acronym: s,
|
||||||
|
address: s,
|
||||||
|
applet: s,
|
||||||
|
area: {
|
||||||
|
attrs: {
|
||||||
|
alt: null, coords: null, href: null, target: null, ping: null,
|
||||||
|
media: media, hreflang: langs, type: null,
|
||||||
|
shape: ["default", "rect", "circle", "poly"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
article: s,
|
||||||
|
aside: s,
|
||||||
|
audio: {
|
||||||
|
attrs: {
|
||||||
|
src: null, mediagroup: null,
|
||||||
|
crossorigin: ["anonymous", "use-credentials"],
|
||||||
|
preload: ["none", "metadata", "auto"],
|
||||||
|
autoplay: ["", "autoplay"],
|
||||||
|
loop: ["", "loop"],
|
||||||
|
controls: ["", "controls"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
b: s,
|
||||||
|
base: { attrs: { href: null, target: targets } },
|
||||||
|
basefont: s,
|
||||||
|
bdi: s,
|
||||||
|
bdo: s,
|
||||||
|
big: s,
|
||||||
|
blockquote: { attrs: { cite: null } },
|
||||||
|
body: s,
|
||||||
|
br: s,
|
||||||
|
button: {
|
||||||
|
attrs: {
|
||||||
|
form: null, formaction: null, name: null, value: null,
|
||||||
|
autofocus: ["", "autofocus"],
|
||||||
|
disabled: ["", "autofocus"],
|
||||||
|
formenctype: encs,
|
||||||
|
formmethod: methods,
|
||||||
|
formnovalidate: ["", "novalidate"],
|
||||||
|
formtarget: targets,
|
||||||
|
type: ["submit", "reset", "button"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canvas: { attrs: { width: null, height: null } },
|
||||||
|
caption: s,
|
||||||
|
center: s,
|
||||||
|
cite: s,
|
||||||
|
code: s,
|
||||||
|
col: { attrs: { span: null } },
|
||||||
|
colgroup: { attrs: { span: null } },
|
||||||
|
command: {
|
||||||
|
attrs: {
|
||||||
|
type: ["command", "checkbox", "radio"],
|
||||||
|
label: null, icon: null, radiogroup: null, command: null, title: null,
|
||||||
|
disabled: ["", "disabled"],
|
||||||
|
checked: ["", "checked"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: { attrs: { value: null } },
|
||||||
|
datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
|
||||||
|
datalist: { attrs: { data: null } },
|
||||||
|
dd: s,
|
||||||
|
del: { attrs: { cite: null, datetime: null } },
|
||||||
|
details: { attrs: { open: ["", "open"] } },
|
||||||
|
dfn: s,
|
||||||
|
dir: s,
|
||||||
|
div: s,
|
||||||
|
dialog: { attrs: { open: null } },
|
||||||
|
dl: s,
|
||||||
|
dt: s,
|
||||||
|
em: s,
|
||||||
|
embed: { attrs: { src: null, type: null, width: null, height: null } },
|
||||||
|
eventsource: { attrs: { src: null } },
|
||||||
|
fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
|
||||||
|
figcaption: s,
|
||||||
|
figure: s,
|
||||||
|
font: s,
|
||||||
|
footer: s,
|
||||||
|
form: {
|
||||||
|
attrs: {
|
||||||
|
action: null, name: null,
|
||||||
|
"accept-charset": charsets,
|
||||||
|
autocomplete: ["on", "off"],
|
||||||
|
enctype: encs,
|
||||||
|
method: methods,
|
||||||
|
novalidate: ["", "novalidate"],
|
||||||
|
target: targets
|
||||||
|
}
|
||||||
|
},
|
||||||
|
frame: s,
|
||||||
|
frameset: s,
|
||||||
|
h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
|
||||||
|
head: {
|
||||||
|
attrs: {},
|
||||||
|
children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
|
||||||
|
},
|
||||||
|
header: s,
|
||||||
|
hgroup: s,
|
||||||
|
hr: s,
|
||||||
|
html: {
|
||||||
|
attrs: { manifest: null },
|
||||||
|
children: ["head", "body"]
|
||||||
|
},
|
||||||
|
i: s,
|
||||||
|
iframe: {
|
||||||
|
attrs: {
|
||||||
|
src: null, srcdoc: null, name: null, width: null, height: null,
|
||||||
|
sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
|
||||||
|
seamless: ["", "seamless"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
attrs: {
|
||||||
|
alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
|
||||||
|
crossorigin: ["anonymous", "use-credentials"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
attrs: {
|
||||||
|
alt: null, dirname: null, form: null, formaction: null,
|
||||||
|
height: null, list: null, max: null, maxlength: null, min: null,
|
||||||
|
name: null, pattern: null, placeholder: null, size: null, src: null,
|
||||||
|
step: null, value: null, width: null,
|
||||||
|
accept: ["audio/*", "video/*", "image/*"],
|
||||||
|
autocomplete: ["on", "off"],
|
||||||
|
autofocus: ["", "autofocus"],
|
||||||
|
checked: ["", "checked"],
|
||||||
|
disabled: ["", "disabled"],
|
||||||
|
formenctype: encs,
|
||||||
|
formmethod: methods,
|
||||||
|
formnovalidate: ["", "novalidate"],
|
||||||
|
formtarget: targets,
|
||||||
|
multiple: ["", "multiple"],
|
||||||
|
readonly: ["", "readonly"],
|
||||||
|
required: ["", "required"],
|
||||||
|
type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
|
||||||
|
"week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
|
||||||
|
"file", "submit", "image", "reset", "button"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ins: { attrs: { cite: null, datetime: null } },
|
||||||
|
kbd: s,
|
||||||
|
keygen: {
|
||||||
|
attrs: {
|
||||||
|
challenge: null, form: null, name: null,
|
||||||
|
autofocus: ["", "autofocus"],
|
||||||
|
disabled: ["", "disabled"],
|
||||||
|
keytype: ["RSA"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: { attrs: { "for": null, form: null } },
|
||||||
|
legend: s,
|
||||||
|
li: { attrs: { value: null } },
|
||||||
|
link: {
|
||||||
|
attrs: {
|
||||||
|
href: null, type: null,
|
||||||
|
hreflang: langs,
|
||||||
|
media: media,
|
||||||
|
sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map: { attrs: { name: null } },
|
||||||
|
mark: s,
|
||||||
|
menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
|
||||||
|
meta: {
|
||||||
|
attrs: {
|
||||||
|
content: null,
|
||||||
|
charset: charsets,
|
||||||
|
name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
|
||||||
|
"http-equiv": ["content-language", "content-type", "default-style", "refresh"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
|
||||||
|
nav: s,
|
||||||
|
noframes: s,
|
||||||
|
noscript: s,
|
||||||
|
object: {
|
||||||
|
attrs: {
|
||||||
|
data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
|
||||||
|
typemustmatch: ["", "typemustmatch"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
|
||||||
|
optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
|
||||||
|
option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
|
||||||
|
output: { attrs: { "for": null, form: null, name: null } },
|
||||||
|
p: s,
|
||||||
|
param: { attrs: { name: null, value: null } },
|
||||||
|
pre: s,
|
||||||
|
progress: { attrs: { value: null, max: null } },
|
||||||
|
q: { attrs: { cite: null } },
|
||||||
|
rp: s,
|
||||||
|
rt: s,
|
||||||
|
ruby: s,
|
||||||
|
s: s,
|
||||||
|
samp: s,
|
||||||
|
script: {
|
||||||
|
attrs: {
|
||||||
|
type: ["text/javascript"],
|
||||||
|
src: null,
|
||||||
|
async: ["", "async"],
|
||||||
|
defer: ["", "defer"],
|
||||||
|
charset: charsets
|
||||||
|
}
|
||||||
|
},
|
||||||
|
section: s,
|
||||||
|
select: {
|
||||||
|
attrs: {
|
||||||
|
form: null, name: null, size: null,
|
||||||
|
autofocus: ["", "autofocus"],
|
||||||
|
disabled: ["", "disabled"],
|
||||||
|
multiple: ["", "multiple"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
small: s,
|
||||||
|
source: { attrs: { src: null, type: null, media: null } },
|
||||||
|
span: s,
|
||||||
|
strike: s,
|
||||||
|
strong: s,
|
||||||
|
style: {
|
||||||
|
attrs: {
|
||||||
|
type: ["text/css"],
|
||||||
|
media: media,
|
||||||
|
scoped: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sub: s,
|
||||||
|
summary: s,
|
||||||
|
sup: s,
|
||||||
|
table: s,
|
||||||
|
tbody: s,
|
||||||
|
td: { attrs: { colspan: null, rowspan: null, headers: null } },
|
||||||
|
textarea: {
|
||||||
|
attrs: {
|
||||||
|
dirname: null, form: null, maxlength: null, name: null, placeholder: null,
|
||||||
|
rows: null, cols: null,
|
||||||
|
autofocus: ["", "autofocus"],
|
||||||
|
disabled: ["", "disabled"],
|
||||||
|
readonly: ["", "readonly"],
|
||||||
|
required: ["", "required"],
|
||||||
|
wrap: ["soft", "hard"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tfoot: s,
|
||||||
|
th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
|
||||||
|
thead: s,
|
||||||
|
time: { attrs: { datetime: null } },
|
||||||
|
title: s,
|
||||||
|
tr: s,
|
||||||
|
track: {
|
||||||
|
attrs: {
|
||||||
|
src: null, label: null, "default": null,
|
||||||
|
kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
|
||||||
|
srclang: langs
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tt: s,
|
||||||
|
u: s,
|
||||||
|
ul: s,
|
||||||
|
"var": s,
|
||||||
|
video: {
|
||||||
|
attrs: {
|
||||||
|
src: null, poster: null, width: null, height: null,
|
||||||
|
crossorigin: ["anonymous", "use-credentials"],
|
||||||
|
preload: ["auto", "metadata", "none"],
|
||||||
|
autoplay: ["", "autoplay"],
|
||||||
|
mediagroup: ["movie"],
|
||||||
|
muted: ["", "muted"],
|
||||||
|
controls: ["", "controls"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wbr: s
|
||||||
|
};
|
||||||
|
|
||||||
|
var globalAttrs = {
|
||||||
|
accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
||||||
|
"class": null,
|
||||||
|
contenteditable: ["true", "false"],
|
||||||
|
contextmenu: null,
|
||||||
|
dir: ["ltr", "rtl", "auto"],
|
||||||
|
draggable: ["true", "false", "auto"],
|
||||||
|
dropzone: ["copy", "move", "link", "string:", "file:"],
|
||||||
|
hidden: ["hidden"],
|
||||||
|
id: null,
|
||||||
|
inert: ["inert"],
|
||||||
|
itemid: null,
|
||||||
|
itemprop: null,
|
||||||
|
itemref: null,
|
||||||
|
itemscope: ["itemscope"],
|
||||||
|
itemtype: null,
|
||||||
|
lang: ["en", "es"],
|
||||||
|
spellcheck: ["true", "false"],
|
||||||
|
autocorrect: ["true", "false"],
|
||||||
|
autocapitalize: ["true", "false"],
|
||||||
|
style: null,
|
||||||
|
tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
||||||
|
title: null,
|
||||||
|
translate: ["yes", "no"],
|
||||||
|
onclick: null,
|
||||||
|
rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
|
||||||
|
};
|
||||||
|
function populate(obj) {
|
||||||
|
for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
|
||||||
|
obj.attrs[attr] = globalAttrs[attr];
|
||||||
|
}
|
||||||
|
|
||||||
|
populate(s);
|
||||||
|
for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
|
||||||
|
populate(data[tag]);
|
||||||
|
|
||||||
|
CodeMirror.htmlSchema = data;
|
||||||
|
function htmlHint(cm, options) {
|
||||||
|
var local = {schemaInfo: data};
|
||||||
|
if (options) for (var opt in options) local[opt] = options[opt];
|
||||||
|
return CodeMirror.hint.xml(cm, local);
|
||||||
|
}
|
||||||
|
CodeMirror.registerHelper("hint", "html", htmlHint);
|
||||||
|
});
|
||||||
162
src/modules/_CodeMirror/addon/hint/javascript-hint.js
Normal file
162
src/modules/_CodeMirror/addon/hint/javascript-hint.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
function forEach(arr, f) {
|
||||||
|
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayContains(arr, item) {
|
||||||
|
if (!Array.prototype.indexOf) {
|
||||||
|
var i = arr.length;
|
||||||
|
while (i--) {
|
||||||
|
if (arr[i] === item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return arr.indexOf(item) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptHint(editor, keywords, getToken, options) {
|
||||||
|
// Find the token at the cursor
|
||||||
|
var cur = editor.getCursor(), token = getToken(editor, cur);
|
||||||
|
if (/\b(?:string|comment)\b/.test(token.type)) return;
|
||||||
|
var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
|
||||||
|
if (innerMode.mode.helperType === "json") return;
|
||||||
|
token.state = innerMode.state;
|
||||||
|
|
||||||
|
// If it's not a 'word-style' token, ignore the token.
|
||||||
|
if (!/^[\w$_]*$/.test(token.string)) {
|
||||||
|
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
||||||
|
type: token.string == "." ? "property" : null};
|
||||||
|
} else if (token.end > cur.ch) {
|
||||||
|
token.end = cur.ch;
|
||||||
|
token.string = token.string.slice(0, cur.ch - token.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tprop = token;
|
||||||
|
// If it is a property, find out what it is a property of.
|
||||||
|
while (tprop.type == "property") {
|
||||||
|
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
||||||
|
if (tprop.string != ".") return;
|
||||||
|
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
||||||
|
if (!context) var context = [];
|
||||||
|
context.push(tprop);
|
||||||
|
}
|
||||||
|
return {list: getCompletions(token, context, keywords, options),
|
||||||
|
from: Pos(cur.line, token.start),
|
||||||
|
to: Pos(cur.line, token.end)};
|
||||||
|
}
|
||||||
|
|
||||||
|
function javascriptHint(editor, options) {
|
||||||
|
return scriptHint(editor, javascriptKeywords,
|
||||||
|
function (e, cur) {return e.getTokenAt(cur);},
|
||||||
|
options);
|
||||||
|
};
|
||||||
|
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
||||||
|
|
||||||
|
function getCoffeeScriptToken(editor, cur) {
|
||||||
|
// This getToken, it is for coffeescript, imitates the behavior of
|
||||||
|
// getTokenAt method in javascript.js, that is, returning "property"
|
||||||
|
// type and treat "." as indepenent token.
|
||||||
|
var token = editor.getTokenAt(cur);
|
||||||
|
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
|
||||||
|
token.end = token.start;
|
||||||
|
token.string = '.';
|
||||||
|
token.type = "property";
|
||||||
|
}
|
||||||
|
else if (/^\.[\w$_]*$/.test(token.string)) {
|
||||||
|
token.type = "property";
|
||||||
|
token.start++;
|
||||||
|
token.string = token.string.replace(/\./, '');
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function coffeescriptHint(editor, options) {
|
||||||
|
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
|
||||||
|
}
|
||||||
|
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
|
||||||
|
|
||||||
|
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
|
||||||
|
"toUpperCase toLowerCase split concat match replace search").split(" ");
|
||||||
|
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
|
||||||
|
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
|
||||||
|
var funcProps = "prototype apply call bind".split(" ");
|
||||||
|
var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
|
||||||
|
"if in import instanceof new null return super switch this throw true try typeof var void while with yield").split(" ");
|
||||||
|
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
|
||||||
|
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
||||||
|
|
||||||
|
function forAllProps(obj, callback) {
|
||||||
|
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
|
||||||
|
for (var name in obj) callback(name)
|
||||||
|
} else {
|
||||||
|
for (var o = obj; o; o = Object.getPrototypeOf(o))
|
||||||
|
Object.getOwnPropertyNames(o).forEach(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCompletions(token, context, keywords, options) {
|
||||||
|
var found = [], start = token.string, global = options && options.globalScope || window;
|
||||||
|
function maybeAdd(str) {
|
||||||
|
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
|
||||||
|
}
|
||||||
|
function gatherCompletions(obj) {
|
||||||
|
if (typeof obj == "string") forEach(stringProps, maybeAdd);
|
||||||
|
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
|
||||||
|
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
|
||||||
|
forAllProps(obj, maybeAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context && context.length) {
|
||||||
|
// If this is a property, see if it belongs to some object we can
|
||||||
|
// find in the current environment.
|
||||||
|
var obj = context.pop(), base;
|
||||||
|
if (obj.type && obj.type.indexOf("variable") === 0) {
|
||||||
|
if (options && options.additionalContext)
|
||||||
|
base = options.additionalContext[obj.string];
|
||||||
|
if (!options || options.useGlobalScope !== false)
|
||||||
|
base = base || global[obj.string];
|
||||||
|
} else if (obj.type == "string") {
|
||||||
|
base = "";
|
||||||
|
} else if (obj.type == "atom") {
|
||||||
|
base = 1;
|
||||||
|
} else if (obj.type == "function") {
|
||||||
|
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
||||||
|
(typeof global.jQuery == 'function'))
|
||||||
|
base = global.jQuery();
|
||||||
|
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
|
||||||
|
base = global._();
|
||||||
|
}
|
||||||
|
while (base != null && context.length)
|
||||||
|
base = base[context.pop().string];
|
||||||
|
if (base != null) gatherCompletions(base);
|
||||||
|
} else {
|
||||||
|
// If not, just look in the global object, any local scope, and optional additional-context
|
||||||
|
// (reading into JS mode internals to get at the local and global variables)
|
||||||
|
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
||||||
|
for (var c = token.state.context; c; c = c.prev)
|
||||||
|
for (var v = c.vars; v; v = v.next) maybeAdd(v.name)
|
||||||
|
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
|
||||||
|
if (options && options.additionalContext != null)
|
||||||
|
for (var key in options.additionalContext)
|
||||||
|
maybeAdd(key);
|
||||||
|
if (!options || options.useGlobalScope !== false)
|
||||||
|
gatherCompletions(global);
|
||||||
|
forEach(keywords, maybeAdd);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
});
|
||||||
36
src/modules/_CodeMirror/addon/hint/show-hint.css
Normal file
36
src/modules/_CodeMirror/addon/hint/show-hint.css
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.CodeMirror-hints {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
font-size: 90%;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
|
max-height: 20em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-hint {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: pre;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.CodeMirror-hint-active {
|
||||||
|
background: #08f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
479
src/modules/_CodeMirror/addon/hint/show-hint.js
Normal file
479
src/modules/_CodeMirror/addon/hint/show-hint.js
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
|
||||||
|
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
|
||||||
|
|
||||||
|
// This is the old interface, kept around for now to stay
|
||||||
|
// backwards-compatible.
|
||||||
|
CodeMirror.showHint = function(cm, getHints, options) {
|
||||||
|
if (!getHints) return cm.showHint(options);
|
||||||
|
if (options && options.async) getHints.async = true;
|
||||||
|
var newOpts = {hint: getHints};
|
||||||
|
if (options) for (var prop in options) newOpts[prop] = options[prop];
|
||||||
|
return cm.showHint(newOpts);
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("showHint", function(options) {
|
||||||
|
options = parseOptions(this, this.getCursor("start"), options);
|
||||||
|
var selections = this.listSelections()
|
||||||
|
if (selections.length > 1) return;
|
||||||
|
// By default, don't allow completion when something is selected.
|
||||||
|
// A hint function can have a `supportsSelection` property to
|
||||||
|
// indicate that it can handle selections.
|
||||||
|
if (this.somethingSelected()) {
|
||||||
|
if (!options.hint.supportsSelection) return;
|
||||||
|
// Don't try with cross-line selections
|
||||||
|
for (var i = 0; i < selections.length; i++)
|
||||||
|
if (selections[i].head.line != selections[i].anchor.line) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.completionActive) this.state.completionActive.close();
|
||||||
|
var completion = this.state.completionActive = new Completion(this, options);
|
||||||
|
if (!completion.options.hint) return;
|
||||||
|
|
||||||
|
CodeMirror.signal(this, "startCompletion", this);
|
||||||
|
completion.update(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("closeHint", function() {
|
||||||
|
if (this.state.completionActive) this.state.completionActive.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
function Completion(cm, options) {
|
||||||
|
this.cm = cm;
|
||||||
|
this.options = options;
|
||||||
|
this.widget = null;
|
||||||
|
this.debounce = 0;
|
||||||
|
this.tick = 0;
|
||||||
|
this.startPos = this.cm.getCursor("start");
|
||||||
|
this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
|
||||||
|
return setTimeout(fn, 1000/60);
|
||||||
|
};
|
||||||
|
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
|
||||||
|
|
||||||
|
Completion.prototype = {
|
||||||
|
close: function() {
|
||||||
|
if (!this.active()) return;
|
||||||
|
this.cm.state.completionActive = null;
|
||||||
|
this.tick = null;
|
||||||
|
this.cm.off("cursorActivity", this.activityFunc);
|
||||||
|
|
||||||
|
if (this.widget && this.data) CodeMirror.signal(this.data, "close");
|
||||||
|
if (this.widget) this.widget.close();
|
||||||
|
CodeMirror.signal(this.cm, "endCompletion", this.cm);
|
||||||
|
},
|
||||||
|
|
||||||
|
active: function() {
|
||||||
|
return this.cm.state.completionActive == this;
|
||||||
|
},
|
||||||
|
|
||||||
|
pick: function(data, i) {
|
||||||
|
var completion = data.list[i], self = this;
|
||||||
|
this.cm.operation(function() {
|
||||||
|
if (completion.hint)
|
||||||
|
completion.hint(self.cm, data, completion);
|
||||||
|
else
|
||||||
|
self.cm.replaceRange(getText(completion), completion.from || data.from,
|
||||||
|
completion.to || data.to, "complete");
|
||||||
|
CodeMirror.signal(data, "pick", completion);
|
||||||
|
self.cm.scrollIntoView();
|
||||||
|
})
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
cursorActivity: function() {
|
||||||
|
if (this.debounce) {
|
||||||
|
cancelAnimationFrame(this.debounce);
|
||||||
|
this.debounce = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var identStart = this.startPos;
|
||||||
|
if(this.data) {
|
||||||
|
identStart = this.data.from;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
|
||||||
|
if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
|
||||||
|
pos.ch < identStart.ch || this.cm.somethingSelected() ||
|
||||||
|
(!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
|
||||||
|
this.close();
|
||||||
|
} else {
|
||||||
|
var self = this;
|
||||||
|
this.debounce = requestAnimationFrame(function() {self.update();});
|
||||||
|
if (this.widget) this.widget.disable();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(first) {
|
||||||
|
if (this.tick == null) return
|
||||||
|
var self = this, myTick = ++this.tick
|
||||||
|
fetchHints(this.options.hint, this.cm, this.options, function(data) {
|
||||||
|
if (self.tick == myTick) self.finishUpdate(data, first)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
finishUpdate: function(data, first) {
|
||||||
|
if (this.data) CodeMirror.signal(this.data, "update");
|
||||||
|
|
||||||
|
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
|
||||||
|
if (this.widget) this.widget.close();
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
if (data && data.list.length) {
|
||||||
|
if (picked && data.list.length == 1) {
|
||||||
|
this.pick(data, 0);
|
||||||
|
} else {
|
||||||
|
this.widget = new Widget(this, data);
|
||||||
|
CodeMirror.signal(data, "shown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseOptions(cm, pos, options) {
|
||||||
|
var editor = cm.options.hintOptions;
|
||||||
|
var out = {};
|
||||||
|
for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
|
||||||
|
if (editor) for (var prop in editor)
|
||||||
|
if (editor[prop] !== undefined) out[prop] = editor[prop];
|
||||||
|
if (options) for (var prop in options)
|
||||||
|
if (options[prop] !== undefined) out[prop] = options[prop];
|
||||||
|
if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getText(completion) {
|
||||||
|
if (typeof completion == "string") return completion;
|
||||||
|
else return completion.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKeyMap(completion, handle) {
|
||||||
|
var baseMap = {
|
||||||
|
Up: function() {handle.moveFocus(-1);},
|
||||||
|
Down: function() {handle.moveFocus(1);},
|
||||||
|
PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
|
||||||
|
PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
|
||||||
|
Home: function() {handle.setFocus(0);},
|
||||||
|
End: function() {handle.setFocus(handle.length - 1);},
|
||||||
|
Enter: handle.pick,
|
||||||
|
Tab: handle.pick,
|
||||||
|
Esc: handle.close
|
||||||
|
};
|
||||||
|
|
||||||
|
var mac = /Mac/.test(navigator.platform);
|
||||||
|
|
||||||
|
if (mac) {
|
||||||
|
baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
|
||||||
|
baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
|
||||||
|
}
|
||||||
|
|
||||||
|
var custom = completion.options.customKeys;
|
||||||
|
var ourMap = custom ? {} : baseMap;
|
||||||
|
function addBinding(key, val) {
|
||||||
|
var bound;
|
||||||
|
if (typeof val != "string")
|
||||||
|
bound = function(cm) { return val(cm, handle); };
|
||||||
|
// This mechanism is deprecated
|
||||||
|
else if (baseMap.hasOwnProperty(val))
|
||||||
|
bound = baseMap[val];
|
||||||
|
else
|
||||||
|
bound = val;
|
||||||
|
ourMap[key] = bound;
|
||||||
|
}
|
||||||
|
if (custom)
|
||||||
|
for (var key in custom) if (custom.hasOwnProperty(key))
|
||||||
|
addBinding(key, custom[key]);
|
||||||
|
var extra = completion.options.extraKeys;
|
||||||
|
if (extra)
|
||||||
|
for (var key in extra) if (extra.hasOwnProperty(key))
|
||||||
|
addBinding(key, extra[key]);
|
||||||
|
return ourMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHintElement(hintsElement, el) {
|
||||||
|
while (el && el != hintsElement) {
|
||||||
|
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
|
||||||
|
el = el.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Widget(completion, data) {
|
||||||
|
this.completion = completion;
|
||||||
|
this.data = data;
|
||||||
|
this.picked = false;
|
||||||
|
var widget = this, cm = completion.cm;
|
||||||
|
var ownerDocument = cm.getInputField().ownerDocument;
|
||||||
|
var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
|
||||||
|
|
||||||
|
var hints = this.hints = ownerDocument.createElement("ul");
|
||||||
|
var theme = completion.cm.options.theme;
|
||||||
|
hints.className = "CodeMirror-hints " + theme;
|
||||||
|
this.selectedHint = data.selectedHint || 0;
|
||||||
|
|
||||||
|
var completions = data.list;
|
||||||
|
for (var i = 0; i < completions.length; ++i) {
|
||||||
|
var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
|
||||||
|
var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
|
||||||
|
if (cur.className != null) className = cur.className + " " + className;
|
||||||
|
elt.className = className;
|
||||||
|
if (cur.render) cur.render(elt, data, cur);
|
||||||
|
else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
|
||||||
|
elt.hintId = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = completion.options.container || ownerDocument.body;
|
||||||
|
var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
|
||||||
|
var left = pos.left, top = pos.bottom, below = true;
|
||||||
|
var offsetLeft = 0, offsetTop = 0;
|
||||||
|
if (container !== ownerDocument.body) {
|
||||||
|
// We offset the cursor position because left and top are relative to the offsetParent's top left corner.
|
||||||
|
var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
|
||||||
|
var offsetParent = isContainerPositioned ? container : container.offsetParent;
|
||||||
|
var offsetParentPosition = offsetParent.getBoundingClientRect();
|
||||||
|
var bodyPosition = ownerDocument.body.getBoundingClientRect();
|
||||||
|
offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
|
||||||
|
offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
|
||||||
|
}
|
||||||
|
hints.style.left = (left - offsetLeft) + "px";
|
||||||
|
hints.style.top = (top - offsetTop) + "px";
|
||||||
|
|
||||||
|
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
|
||||||
|
var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
|
||||||
|
var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
|
||||||
|
container.appendChild(hints);
|
||||||
|
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
|
||||||
|
var scrolls = hints.scrollHeight > hints.clientHeight + 1
|
||||||
|
var startScroll = cm.getScrollInfo();
|
||||||
|
|
||||||
|
if (overlapY > 0) {
|
||||||
|
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
|
||||||
|
if (curTop - height > 0) { // Fits above cursor
|
||||||
|
hints.style.top = (top = pos.top - height - offsetTop) + "px";
|
||||||
|
below = false;
|
||||||
|
} else if (height > winH) {
|
||||||
|
hints.style.height = (winH - 5) + "px";
|
||||||
|
hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
|
||||||
|
var cursor = cm.getCursor();
|
||||||
|
if (data.from.ch != cursor.ch) {
|
||||||
|
pos = cm.cursorCoords(cursor);
|
||||||
|
hints.style.left = (left = pos.left - offsetLeft) + "px";
|
||||||
|
box = hints.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var overlapX = box.right - winW;
|
||||||
|
if (overlapX > 0) {
|
||||||
|
if (box.right - box.left > winW) {
|
||||||
|
hints.style.width = (winW - 5) + "px";
|
||||||
|
overlapX -= (box.right - box.left) - winW;
|
||||||
|
}
|
||||||
|
hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
|
||||||
|
}
|
||||||
|
if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
|
||||||
|
node.style.paddingRight = cm.display.nativeBarWidth + "px"
|
||||||
|
|
||||||
|
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
|
||||||
|
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
|
||||||
|
setFocus: function(n) { widget.changeActive(n); },
|
||||||
|
menuSize: function() { return widget.screenAmount(); },
|
||||||
|
length: completions.length,
|
||||||
|
close: function() { completion.close(); },
|
||||||
|
pick: function() { widget.pick(); },
|
||||||
|
data: data
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (completion.options.closeOnUnfocus) {
|
||||||
|
var closingOnBlur;
|
||||||
|
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
|
||||||
|
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.on("scroll", this.onScroll = function() {
|
||||||
|
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
|
||||||
|
var newTop = top + startScroll.top - curScroll.top;
|
||||||
|
var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
|
||||||
|
if (!below) point += hints.offsetHeight;
|
||||||
|
if (point <= editor.top || point >= editor.bottom) return completion.close();
|
||||||
|
hints.style.top = newTop + "px";
|
||||||
|
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.on(hints, "dblclick", function(e) {
|
||||||
|
var t = getHintElement(hints, e.target || e.srcElement);
|
||||||
|
if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.on(hints, "click", function(e) {
|
||||||
|
var t = getHintElement(hints, e.target || e.srcElement);
|
||||||
|
if (t && t.hintId != null) {
|
||||||
|
widget.changeActive(t.hintId);
|
||||||
|
if (completion.options.completeOnSingleClick) widget.pick();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.on(hints, "mousedown", function() {
|
||||||
|
setTimeout(function(){cm.focus();}, 20);
|
||||||
|
});
|
||||||
|
this.scrollToActive()
|
||||||
|
|
||||||
|
CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget.prototype = {
|
||||||
|
close: function() {
|
||||||
|
if (this.completion.widget != this) return;
|
||||||
|
this.completion.widget = null;
|
||||||
|
this.hints.parentNode.removeChild(this.hints);
|
||||||
|
this.completion.cm.removeKeyMap(this.keyMap);
|
||||||
|
|
||||||
|
var cm = this.completion.cm;
|
||||||
|
if (this.completion.options.closeOnUnfocus) {
|
||||||
|
cm.off("blur", this.onBlur);
|
||||||
|
cm.off("focus", this.onFocus);
|
||||||
|
}
|
||||||
|
cm.off("scroll", this.onScroll);
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this.completion.cm.removeKeyMap(this.keyMap);
|
||||||
|
var widget = this;
|
||||||
|
this.keyMap = {Enter: function() { widget.picked = true; }};
|
||||||
|
this.completion.cm.addKeyMap(this.keyMap);
|
||||||
|
},
|
||||||
|
|
||||||
|
pick: function() {
|
||||||
|
this.completion.pick(this.data, this.selectedHint);
|
||||||
|
},
|
||||||
|
|
||||||
|
changeActive: function(i, avoidWrap) {
|
||||||
|
if (i >= this.data.list.length)
|
||||||
|
i = avoidWrap ? this.data.list.length - 1 : 0;
|
||||||
|
else if (i < 0)
|
||||||
|
i = avoidWrap ? 0 : this.data.list.length - 1;
|
||||||
|
if (this.selectedHint == i) return;
|
||||||
|
var node = this.hints.childNodes[this.selectedHint];
|
||||||
|
if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
|
||||||
|
node = this.hints.childNodes[this.selectedHint = i];
|
||||||
|
node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
|
||||||
|
this.scrollToActive()
|
||||||
|
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToActive: function() {
|
||||||
|
var margin = this.completion.options.scrollMargin || 0;
|
||||||
|
var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
|
||||||
|
var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
|
||||||
|
var firstNode = this.hints.firstChild;
|
||||||
|
if (node1.offsetTop < this.hints.scrollTop)
|
||||||
|
this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
|
||||||
|
else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
|
||||||
|
this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;
|
||||||
|
},
|
||||||
|
|
||||||
|
screenAmount: function() {
|
||||||
|
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function applicableHelpers(cm, helpers) {
|
||||||
|
if (!cm.somethingSelected()) return helpers
|
||||||
|
var result = []
|
||||||
|
for (var i = 0; i < helpers.length; i++)
|
||||||
|
if (helpers[i].supportsSelection) result.push(helpers[i])
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchHints(hint, cm, options, callback) {
|
||||||
|
if (hint.async) {
|
||||||
|
hint(cm, callback, options)
|
||||||
|
} else {
|
||||||
|
var result = hint(cm, options)
|
||||||
|
if (result && result.then) result.then(callback)
|
||||||
|
else callback(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAutoHints(cm, pos) {
|
||||||
|
var helpers = cm.getHelpers(pos, "hint"), words
|
||||||
|
if (helpers.length) {
|
||||||
|
var resolved = function(cm, callback, options) {
|
||||||
|
var app = applicableHelpers(cm, helpers);
|
||||||
|
function run(i) {
|
||||||
|
if (i == app.length) return callback(null)
|
||||||
|
fetchHints(app[i], cm, options, function(result) {
|
||||||
|
if (result && result.list.length > 0) callback(result)
|
||||||
|
else run(i + 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
run(0)
|
||||||
|
}
|
||||||
|
resolved.async = true
|
||||||
|
resolved.supportsSelection = true
|
||||||
|
return resolved
|
||||||
|
} else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
|
||||||
|
return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
|
||||||
|
} else if (CodeMirror.hint.anyword) {
|
||||||
|
return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
|
||||||
|
} else {
|
||||||
|
return function() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "auto", {
|
||||||
|
resolve: resolveAutoHints
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
|
||||||
|
var cur = cm.getCursor(), token = cm.getTokenAt(cur)
|
||||||
|
var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
|
||||||
|
if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
|
||||||
|
term = token.string.substr(0, cur.ch - token.start)
|
||||||
|
} else {
|
||||||
|
term = ""
|
||||||
|
from = cur
|
||||||
|
}
|
||||||
|
var found = [];
|
||||||
|
for (var i = 0; i < options.words.length; i++) {
|
||||||
|
var word = options.words[i];
|
||||||
|
if (word.slice(0, term.length) == term)
|
||||||
|
found.push(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found.length) return {list: found, from: from, to: to};
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.commands.autocomplete = CodeMirror.showHint;
|
||||||
|
|
||||||
|
var defaultOptions = {
|
||||||
|
hint: CodeMirror.hint.auto,
|
||||||
|
completeSingle: true,
|
||||||
|
alignWithWord: true,
|
||||||
|
closeCharacters: /[\s()\[\]{};:>,]/,
|
||||||
|
closeOnUnfocus: true,
|
||||||
|
completeOnSingleClick: true,
|
||||||
|
container: null,
|
||||||
|
customKeys: null,
|
||||||
|
extraKeys: null
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.defineOption("hintOptions", null);
|
||||||
|
});
|
||||||
304
src/modules/_CodeMirror/addon/hint/sql-hint.js
Normal file
304
src/modules/_CodeMirror/addon/hint/sql-hint.js
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var tables;
|
||||||
|
var defaultTable;
|
||||||
|
var keywords;
|
||||||
|
var identifierQuote;
|
||||||
|
var CONS = {
|
||||||
|
QUERY_DIV: ";",
|
||||||
|
ALIAS_KEYWORD: "AS"
|
||||||
|
};
|
||||||
|
var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
|
||||||
|
|
||||||
|
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
|
||||||
|
|
||||||
|
function getKeywords(editor) {
|
||||||
|
var mode = editor.doc.modeOption;
|
||||||
|
if (mode === "sql") mode = "text/x-sql";
|
||||||
|
return CodeMirror.resolveMode(mode).keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIdentifierQuote(editor) {
|
||||||
|
var mode = editor.doc.modeOption;
|
||||||
|
if (mode === "sql") mode = "text/x-sql";
|
||||||
|
return CodeMirror.resolveMode(mode).identifierQuote || "`";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getText(item) {
|
||||||
|
return typeof item == "string" ? item : item.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapTable(name, value) {
|
||||||
|
if (isArray(value)) value = {columns: value}
|
||||||
|
if (!value.text) value.text = name
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTables(input) {
|
||||||
|
var result = {}
|
||||||
|
if (isArray(input)) {
|
||||||
|
for (var i = input.length - 1; i >= 0; i--) {
|
||||||
|
var item = input[i]
|
||||||
|
result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
|
||||||
|
}
|
||||||
|
} else if (input) {
|
||||||
|
for (var name in input)
|
||||||
|
result[name.toUpperCase()] = wrapTable(name, input[name])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTable(name) {
|
||||||
|
return tables[name.toUpperCase()]
|
||||||
|
}
|
||||||
|
|
||||||
|
function shallowClone(object) {
|
||||||
|
var result = {};
|
||||||
|
for (var key in object) if (object.hasOwnProperty(key))
|
||||||
|
result[key] = object[key];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function match(string, word) {
|
||||||
|
var len = string.length;
|
||||||
|
var sub = getText(word).substr(0, len);
|
||||||
|
return string.toUpperCase() === sub.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMatches(result, search, wordlist, formatter) {
|
||||||
|
if (isArray(wordlist)) {
|
||||||
|
for (var i = 0; i < wordlist.length; i++)
|
||||||
|
if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
|
||||||
|
} else {
|
||||||
|
for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
|
||||||
|
var val = wordlist[word]
|
||||||
|
if (!val || val === true)
|
||||||
|
val = word
|
||||||
|
else
|
||||||
|
val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
|
||||||
|
if (match(search, val)) result.push(formatter(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanName(name) {
|
||||||
|
// Get rid name from identifierQuote and preceding dot(.)
|
||||||
|
if (name.charAt(0) == ".") {
|
||||||
|
name = name.substr(1);
|
||||||
|
}
|
||||||
|
// replace doublicated identifierQuotes with single identifierQuotes
|
||||||
|
// and remove single identifierQuotes
|
||||||
|
var nameParts = name.split(identifierQuote+identifierQuote);
|
||||||
|
for (var i = 0; i < nameParts.length; i++)
|
||||||
|
nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
|
||||||
|
return nameParts.join(identifierQuote);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertIdentifierQuotes(name) {
|
||||||
|
var nameParts = getText(name).split(".");
|
||||||
|
for (var i = 0; i < nameParts.length; i++)
|
||||||
|
nameParts[i] = identifierQuote +
|
||||||
|
// doublicate identifierQuotes
|
||||||
|
nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
|
||||||
|
identifierQuote;
|
||||||
|
var escaped = nameParts.join(".");
|
||||||
|
if (typeof name == "string") return escaped;
|
||||||
|
name = shallowClone(name);
|
||||||
|
name.text = escaped;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nameCompletion(cur, token, result, editor) {
|
||||||
|
// Try to complete table, column names and return start position of completion
|
||||||
|
var useIdentifierQuotes = false;
|
||||||
|
var nameParts = [];
|
||||||
|
var start = token.start;
|
||||||
|
var cont = true;
|
||||||
|
while (cont) {
|
||||||
|
cont = (token.string.charAt(0) == ".");
|
||||||
|
useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
|
||||||
|
|
||||||
|
start = token.start;
|
||||||
|
nameParts.unshift(cleanName(token.string));
|
||||||
|
|
||||||
|
token = editor.getTokenAt(Pos(cur.line, token.start));
|
||||||
|
if (token.string == ".") {
|
||||||
|
cont = true;
|
||||||
|
token = editor.getTokenAt(Pos(cur.line, token.start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to complete table names
|
||||||
|
var string = nameParts.join(".");
|
||||||
|
addMatches(result, string, tables, function(w) {
|
||||||
|
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to complete columns from defaultTable
|
||||||
|
addMatches(result, string, defaultTable, function(w) {
|
||||||
|
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to complete columns
|
||||||
|
string = nameParts.pop();
|
||||||
|
var table = nameParts.join(".");
|
||||||
|
|
||||||
|
var alias = false;
|
||||||
|
var aliasTable = table;
|
||||||
|
// Check if table is available. If not, find table by Alias
|
||||||
|
if (!getTable(table)) {
|
||||||
|
var oldTable = table;
|
||||||
|
table = findTableByAlias(table, editor);
|
||||||
|
if (table !== oldTable) alias = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var columns = getTable(table);
|
||||||
|
if (columns && columns.columns)
|
||||||
|
columns = columns.columns;
|
||||||
|
|
||||||
|
if (columns) {
|
||||||
|
addMatches(result, string, columns, function(w) {
|
||||||
|
var tableInsert = table;
|
||||||
|
if (alias == true) tableInsert = aliasTable;
|
||||||
|
if (typeof w == "string") {
|
||||||
|
w = tableInsert + "." + w;
|
||||||
|
} else {
|
||||||
|
w = shallowClone(w);
|
||||||
|
w.text = tableInsert + "." + w.text;
|
||||||
|
}
|
||||||
|
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
function eachWord(lineText, f) {
|
||||||
|
var words = lineText.split(/\s+/)
|
||||||
|
for (var i = 0; i < words.length; i++)
|
||||||
|
if (words[i]) f(words[i].replace(/[`,;]/g, ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTableByAlias(alias, editor) {
|
||||||
|
var doc = editor.doc;
|
||||||
|
var fullQuery = doc.getValue();
|
||||||
|
var aliasUpperCase = alias.toUpperCase();
|
||||||
|
var previousWord = "";
|
||||||
|
var table = "";
|
||||||
|
var separator = [];
|
||||||
|
var validRange = {
|
||||||
|
start: Pos(0, 0),
|
||||||
|
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
|
||||||
|
};
|
||||||
|
|
||||||
|
//add separator
|
||||||
|
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
|
||||||
|
while(indexOfSeparator != -1) {
|
||||||
|
separator.push(doc.posFromIndex(indexOfSeparator));
|
||||||
|
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
|
||||||
|
}
|
||||||
|
separator.unshift(Pos(0, 0));
|
||||||
|
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
|
||||||
|
|
||||||
|
//find valid range
|
||||||
|
var prevItem = null;
|
||||||
|
var current = editor.getCursor()
|
||||||
|
for (var i = 0; i < separator.length; i++) {
|
||||||
|
if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
|
||||||
|
validRange = {start: prevItem, end: separator[i]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prevItem = separator[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validRange.start) {
|
||||||
|
var query = doc.getRange(validRange.start, validRange.end, false);
|
||||||
|
|
||||||
|
for (var i = 0; i < query.length; i++) {
|
||||||
|
var lineText = query[i];
|
||||||
|
eachWord(lineText, function(word) {
|
||||||
|
var wordUpperCase = word.toUpperCase();
|
||||||
|
if (wordUpperCase === aliasUpperCase && getTable(previousWord))
|
||||||
|
table = previousWord;
|
||||||
|
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
|
||||||
|
previousWord = word;
|
||||||
|
});
|
||||||
|
if (table) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
|
||||||
|
tables = parseTables(options && options.tables)
|
||||||
|
var defaultTableName = options && options.defaultTable;
|
||||||
|
var disableKeywords = options && options.disableKeywords;
|
||||||
|
defaultTable = defaultTableName && getTable(defaultTableName);
|
||||||
|
keywords = getKeywords(editor);
|
||||||
|
identifierQuote = getIdentifierQuote(editor);
|
||||||
|
|
||||||
|
if (defaultTableName && !defaultTable)
|
||||||
|
defaultTable = findTableByAlias(defaultTableName, editor);
|
||||||
|
|
||||||
|
defaultTable = defaultTable || [];
|
||||||
|
|
||||||
|
if (defaultTable.columns)
|
||||||
|
defaultTable = defaultTable.columns;
|
||||||
|
|
||||||
|
var cur = editor.getCursor();
|
||||||
|
var result = [];
|
||||||
|
var token = editor.getTokenAt(cur), start, end, search;
|
||||||
|
if (token.end > cur.ch) {
|
||||||
|
token.end = cur.ch;
|
||||||
|
token.string = token.string.slice(0, cur.ch - token.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
|
||||||
|
search = token.string;
|
||||||
|
start = token.start;
|
||||||
|
end = token.end;
|
||||||
|
} else {
|
||||||
|
start = end = cur.ch;
|
||||||
|
search = "";
|
||||||
|
}
|
||||||
|
if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
|
||||||
|
start = nameCompletion(cur, token, result, editor);
|
||||||
|
} else {
|
||||||
|
var objectOrClass = function(w, className) {
|
||||||
|
if (typeof w === "object") {
|
||||||
|
w.className = className;
|
||||||
|
} else {
|
||||||
|
w = { text: w, className: className };
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
addMatches(result, search, defaultTable, function(w) {
|
||||||
|
return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
|
||||||
|
});
|
||||||
|
addMatches(
|
||||||
|
result,
|
||||||
|
search,
|
||||||
|
tables, function(w) {
|
||||||
|
return objectOrClass(w, "CodeMirror-hint-table");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!disableKeywords)
|
||||||
|
addMatches(result, search, keywords, function(w) {
|
||||||
|
return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
|
||||||
|
});
|
||||||
|
});
|
||||||
132
src/modules/_CodeMirror/addon/hint/xml-hint.js
Normal file
132
src/modules/_CodeMirror/addon/hint/xml-hint.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
function matches(hint, typed, matchInMiddle) {
|
||||||
|
if (matchInMiddle) return hint.indexOf(typed) >= 0;
|
||||||
|
else return hint.lastIndexOf(typed, 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHints(cm, options) {
|
||||||
|
var tags = options && options.schemaInfo;
|
||||||
|
var quote = (options && options.quoteChar) || '"';
|
||||||
|
var matchInMiddle = options && options.matchInMiddle;
|
||||||
|
if (!tags) return;
|
||||||
|
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
||||||
|
if (token.end > cur.ch) {
|
||||||
|
token.end = cur.ch;
|
||||||
|
token.string = token.string.slice(0, cur.ch - token.start);
|
||||||
|
}
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
||||||
|
if (!inner.mode.xmlCurrentTag) return
|
||||||
|
var result = [], replaceToken = false, prefix;
|
||||||
|
var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
|
||||||
|
var tagName = tag && /^\w/.test(token.string), tagStart;
|
||||||
|
|
||||||
|
if (tagName) {
|
||||||
|
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
|
||||||
|
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
|
||||||
|
if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
|
||||||
|
} else if (tag && token.string == "<") {
|
||||||
|
tagType = "open";
|
||||||
|
} else if (tag && token.string == "</") {
|
||||||
|
tagType = "close";
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagInfo = inner.mode.xmlCurrentTag(inner.state)
|
||||||
|
if (!tag && !tagInfo || tagType) {
|
||||||
|
if (tagName)
|
||||||
|
prefix = token.string;
|
||||||
|
replaceToken = tagType;
|
||||||
|
var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
|
||||||
|
var inner = context.length && context[context.length - 1]
|
||||||
|
var curTag = inner && tags[inner]
|
||||||
|
var childList = inner ? curTag && curTag.children : tags["!top"];
|
||||||
|
if (childList && tagType != "close") {
|
||||||
|
for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))
|
||||||
|
result.push("<" + childList[i]);
|
||||||
|
} else if (tagType != "close") {
|
||||||
|
for (var name in tags)
|
||||||
|
if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle)))
|
||||||
|
result.push("<" + name);
|
||||||
|
}
|
||||||
|
if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle)))
|
||||||
|
result.push("</" + inner + ">");
|
||||||
|
} else {
|
||||||
|
// Attribute completion
|
||||||
|
var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;
|
||||||
|
var globalAttrs = tags["!attrs"];
|
||||||
|
if (!attrs && !globalAttrs) return;
|
||||||
|
if (!attrs) {
|
||||||
|
attrs = globalAttrs;
|
||||||
|
} else if (globalAttrs) { // Combine tag-local and global attributes
|
||||||
|
var set = {};
|
||||||
|
for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
|
||||||
|
for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
|
||||||
|
attrs = set;
|
||||||
|
}
|
||||||
|
if (token.type == "string" || token.string == "=") { // A value
|
||||||
|
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
|
||||||
|
Pos(cur.line, token.type == "string" ? token.start : token.end));
|
||||||
|
var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
|
||||||
|
if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
|
||||||
|
if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
|
||||||
|
if (token.type == "string") {
|
||||||
|
prefix = token.string;
|
||||||
|
var n = 0;
|
||||||
|
if (/['"]/.test(token.string.charAt(0))) {
|
||||||
|
quote = token.string.charAt(0);
|
||||||
|
prefix = token.string.slice(1);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
var len = token.string.length;
|
||||||
|
if (/['"]/.test(token.string.charAt(len - 1))) {
|
||||||
|
quote = token.string.charAt(len - 1);
|
||||||
|
prefix = token.string.substr(n, len - 2);
|
||||||
|
}
|
||||||
|
if (n) { // an opening quote
|
||||||
|
var line = cm.getLine(cur.line);
|
||||||
|
if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote
|
||||||
|
}
|
||||||
|
replaceToken = true;
|
||||||
|
}
|
||||||
|
var returnHintsFromAtValues = function(atValues) {
|
||||||
|
if (atValues)
|
||||||
|
for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))
|
||||||
|
result.push(quote + atValues[i] + quote);
|
||||||
|
return returnHints();
|
||||||
|
};
|
||||||
|
if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues);
|
||||||
|
return returnHintsFromAtValues(atValues);
|
||||||
|
} else { // An attribute name
|
||||||
|
if (token.type == "attribute") {
|
||||||
|
prefix = token.string;
|
||||||
|
replaceToken = true;
|
||||||
|
}
|
||||||
|
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))
|
||||||
|
result.push(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function returnHints() {
|
||||||
|
return {
|
||||||
|
list: result,
|
||||||
|
from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
|
||||||
|
to: replaceToken ? Pos(cur.line, token.end) : cur
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return returnHints();
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "xml", getHints);
|
||||||
|
});
|
||||||
47
src/modules/_CodeMirror/addon/lint/coffeescript-lint.js
Normal file
47
src/modules/_CodeMirror/addon/lint/coffeescript-lint.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
|
||||||
|
|
||||||
|
// declare global: coffeelint
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "coffeescript", function(text) {
|
||||||
|
var found = [];
|
||||||
|
if (!window.coffeelint) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
var parseError = function(err) {
|
||||||
|
var loc = err.lineNumber;
|
||||||
|
found.push({from: CodeMirror.Pos(loc-1, 0),
|
||||||
|
to: CodeMirror.Pos(loc, 0),
|
||||||
|
severity: err.level,
|
||||||
|
message: err.message});
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
var res = coffeelint.lint(text);
|
||||||
|
for(var i = 0; i < res.length; i++) {
|
||||||
|
parseError(res[i]);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
|
||||||
|
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
|
||||||
|
severity: 'error',
|
||||||
|
message: e.message});
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
40
src/modules/_CodeMirror/addon/lint/css-lint.js
Normal file
40
src/modules/_CodeMirror/addon/lint/css-lint.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Depends on csslint.js from https://github.com/stubbornella/csslint
|
||||||
|
|
||||||
|
// declare global: CSSLint
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "css", function(text, options) {
|
||||||
|
var found = [];
|
||||||
|
if (!window.CSSLint) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
var results = CSSLint.verify(text, options), messages = results.messages, message = null;
|
||||||
|
for ( var i = 0; i < messages.length; i++) {
|
||||||
|
message = messages[i];
|
||||||
|
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
|
||||||
|
found.push({
|
||||||
|
from: CodeMirror.Pos(startLine, startCol),
|
||||||
|
to: CodeMirror.Pos(endLine, endCol),
|
||||||
|
message: message.message,
|
||||||
|
severity : message.type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
59
src/modules/_CodeMirror/addon/lint/html-lint.js
Normal file
59
src/modules/_CodeMirror/addon/lint/html-lint.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js
|
||||||
|
|
||||||
|
// declare global: HTMLHint
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("htmlhint"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "htmlhint"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror, window.HTMLHint);
|
||||||
|
})(function(CodeMirror, HTMLHint) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var defaultRules = {
|
||||||
|
"tagname-lowercase": true,
|
||||||
|
"attr-lowercase": true,
|
||||||
|
"attr-value-double-quotes": true,
|
||||||
|
"doctype-first": false,
|
||||||
|
"tag-pair": true,
|
||||||
|
"spec-char-escape": true,
|
||||||
|
"id-unique": true,
|
||||||
|
"src-not-empty": true,
|
||||||
|
"attr-no-duplication": true
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "html", function(text, options) {
|
||||||
|
var found = [];
|
||||||
|
if (HTMLHint && !HTMLHint.verify) {
|
||||||
|
if(typeof HTMLHint.default !== 'undefined') {
|
||||||
|
HTMLHint = HTMLHint.default;
|
||||||
|
} else {
|
||||||
|
HTMLHint = HTMLHint.HTMLHint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!HTMLHint) HTMLHint = window.HTMLHint;
|
||||||
|
if (!HTMLHint) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run.");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
var messages = HTMLHint.verify(text, options && options.rules || defaultRules);
|
||||||
|
for (var i = 0; i < messages.length; i++) {
|
||||||
|
var message = messages[i];
|
||||||
|
var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col;
|
||||||
|
found.push({
|
||||||
|
from: CodeMirror.Pos(startLine, startCol),
|
||||||
|
to: CodeMirror.Pos(endLine, endCol),
|
||||||
|
message: message.message,
|
||||||
|
severity : message.type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
});
|
||||||
65
src/modules/_CodeMirror/addon/lint/javascript-lint.js
Normal file
65
src/modules/_CodeMirror/addon/lint/javascript-lint.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Depends on jshint.js from https://github.com/jshint/jshint
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
// declare global: JSHINT
|
||||||
|
|
||||||
|
function validator(text, options) {
|
||||||
|
if (!window.JSHINT) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
|
||||||
|
options.indent = 1; // JSHint default value is 4
|
||||||
|
JSHINT(text, options, options.globals);
|
||||||
|
var errors = JSHINT.data().errors, result = [];
|
||||||
|
if (errors) parseErrors(errors, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "javascript", validator);
|
||||||
|
|
||||||
|
function parseErrors(errors, output) {
|
||||||
|
for ( var i = 0; i < errors.length; i++) {
|
||||||
|
var error = errors[i];
|
||||||
|
if (error) {
|
||||||
|
if (error.line <= 0) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var start = error.character - 1, end = start + 1;
|
||||||
|
if (error.evidence) {
|
||||||
|
var index = error.evidence.substring(start).search(/.\b/);
|
||||||
|
if (index > -1) {
|
||||||
|
end += index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to format expected by validation service
|
||||||
|
var hint = {
|
||||||
|
message: error.reason,
|
||||||
|
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
|
||||||
|
from: CodeMirror.Pos(error.line - 1, start),
|
||||||
|
to: CodeMirror.Pos(error.line - 1, end)
|
||||||
|
};
|
||||||
|
|
||||||
|
output.push(hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
40
src/modules/_CodeMirror/addon/lint/json-lint.js
Normal file
40
src/modules/_CodeMirror/addon/lint/json-lint.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
|
||||||
|
|
||||||
|
// declare global: jsonlint
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "json", function(text) {
|
||||||
|
var found = [];
|
||||||
|
if (!window.jsonlint) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
// for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
|
||||||
|
// is a subproperty
|
||||||
|
var jsonlint = window.jsonlint.parser || window.jsonlint
|
||||||
|
jsonlint.parseError = function(str, hash) {
|
||||||
|
var loc = hash.loc;
|
||||||
|
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
|
||||||
|
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
|
||||||
|
message: str});
|
||||||
|
};
|
||||||
|
try { jsonlint.parse(text); }
|
||||||
|
catch(e) {}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
71
src/modules/_CodeMirror/addon/lint/lint.css
Normal file
71
src/modules/_CodeMirror/addon/lint/lint.css
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* The lint marker gutter */
|
||||||
|
.CodeMirror-lint-markers {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-tooltip {
|
||||||
|
background-color: #ffd;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 4px 4px 4px 4px;
|
||||||
|
color: black;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2px 5px;
|
||||||
|
position: fixed;
|
||||||
|
white-space: pre;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
z-index: 100;
|
||||||
|
max-width: 600px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .4s;
|
||||||
|
-moz-transition: opacity .4s;
|
||||||
|
-webkit-transition: opacity .4s;
|
||||||
|
-o-transition: opacity .4s;
|
||||||
|
-ms-transition: opacity .4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-mark {
|
||||||
|
background-position: left bottom;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-mark-warning {
|
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-mark-error {
|
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-marker {
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-message {
|
||||||
|
padding-left: 18px;
|
||||||
|
background-position: top left;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
|
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
|
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lint-marker-multiple {
|
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right bottom;
|
||||||
|
width: 100%; height: 100%;
|
||||||
|
}
|
||||||
255
src/modules/_CodeMirror/addon/lint/lint.js
Normal file
255
src/modules/_CodeMirror/addon/lint/lint.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
var GUTTER_ID = "CodeMirror-lint-markers";
|
||||||
|
|
||||||
|
function showTooltip(cm, e, content) {
|
||||||
|
var tt = document.createElement("div");
|
||||||
|
tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
|
||||||
|
tt.appendChild(content.cloneNode(true));
|
||||||
|
if (cm.state.lint.options.selfContain)
|
||||||
|
cm.getWrapperElement().appendChild(tt);
|
||||||
|
else
|
||||||
|
document.body.appendChild(tt);
|
||||||
|
|
||||||
|
function position(e) {
|
||||||
|
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
|
||||||
|
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
|
||||||
|
tt.style.left = (e.clientX + 5) + "px";
|
||||||
|
}
|
||||||
|
CodeMirror.on(document, "mousemove", position);
|
||||||
|
position(e);
|
||||||
|
if (tt.style.opacity != null) tt.style.opacity = 1;
|
||||||
|
return tt;
|
||||||
|
}
|
||||||
|
function rm(elt) {
|
||||||
|
if (elt.parentNode) elt.parentNode.removeChild(elt);
|
||||||
|
}
|
||||||
|
function hideTooltip(tt) {
|
||||||
|
if (!tt.parentNode) return;
|
||||||
|
if (tt.style.opacity == null) rm(tt);
|
||||||
|
tt.style.opacity = 0;
|
||||||
|
setTimeout(function() { rm(tt); }, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showTooltipFor(cm, e, content, node) {
|
||||||
|
var tooltip = showTooltip(cm, e, content);
|
||||||
|
function hide() {
|
||||||
|
CodeMirror.off(node, "mouseout", hide);
|
||||||
|
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
|
||||||
|
}
|
||||||
|
var poll = setInterval(function() {
|
||||||
|
if (tooltip) for (var n = node;; n = n.parentNode) {
|
||||||
|
if (n && n.nodeType == 11) n = n.host;
|
||||||
|
if (n == document.body) return;
|
||||||
|
if (!n) { hide(); break; }
|
||||||
|
}
|
||||||
|
if (!tooltip) return clearInterval(poll);
|
||||||
|
}, 400);
|
||||||
|
CodeMirror.on(node, "mouseout", hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LintState(cm, options, hasGutter) {
|
||||||
|
this.marked = [];
|
||||||
|
this.options = options;
|
||||||
|
this.timeout = null;
|
||||||
|
this.hasGutter = hasGutter;
|
||||||
|
this.onMouseOver = function(e) { onMouseOver(cm, e); };
|
||||||
|
this.waitingFor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOptions(_cm, options) {
|
||||||
|
if (options instanceof Function) return {getAnnotations: options};
|
||||||
|
if (!options || options === true) options = {};
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearMarks(cm) {
|
||||||
|
var state = cm.state.lint;
|
||||||
|
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
|
||||||
|
for (var i = 0; i < state.marked.length; ++i)
|
||||||
|
state.marked[i].clear();
|
||||||
|
state.marked.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeMarker(cm, labels, severity, multiple, tooltips) {
|
||||||
|
var marker = document.createElement("div"), inner = marker;
|
||||||
|
marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity;
|
||||||
|
if (multiple) {
|
||||||
|
inner = marker.appendChild(document.createElement("div"));
|
||||||
|
inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
|
||||||
|
showTooltipFor(cm, e, labels, inner);
|
||||||
|
});
|
||||||
|
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaxSeverity(a, b) {
|
||||||
|
if (a == "error") return a;
|
||||||
|
else return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupByLine(annotations) {
|
||||||
|
var lines = [];
|
||||||
|
for (var i = 0; i < annotations.length; ++i) {
|
||||||
|
var ann = annotations[i], line = ann.from.line;
|
||||||
|
(lines[line] || (lines[line] = [])).push(ann);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function annotationTooltip(ann) {
|
||||||
|
var severity = ann.severity;
|
||||||
|
if (!severity) severity = "error";
|
||||||
|
var tip = document.createElement("div");
|
||||||
|
tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity;
|
||||||
|
if (typeof ann.messageHTML != 'undefined') {
|
||||||
|
tip.innerHTML = ann.messageHTML;
|
||||||
|
} else {
|
||||||
|
tip.appendChild(document.createTextNode(ann.message));
|
||||||
|
}
|
||||||
|
return tip;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lintAsync(cm, getAnnotations, passOptions) {
|
||||||
|
var state = cm.state.lint
|
||||||
|
var id = ++state.waitingFor
|
||||||
|
function abort() {
|
||||||
|
id = -1
|
||||||
|
cm.off("change", abort)
|
||||||
|
}
|
||||||
|
cm.on("change", abort)
|
||||||
|
getAnnotations(cm.getValue(), function(annotations, arg2) {
|
||||||
|
cm.off("change", abort)
|
||||||
|
if (state.waitingFor != id) return
|
||||||
|
if (arg2 && annotations instanceof CodeMirror) annotations = arg2
|
||||||
|
cm.operation(function() {updateLinting(cm, annotations)})
|
||||||
|
}, passOptions, cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLinting(cm) {
|
||||||
|
var state = cm.state.lint, options = state.options;
|
||||||
|
/*
|
||||||
|
* Passing rules in `options` property prevents JSHint (and other linters) from complaining
|
||||||
|
* about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
|
||||||
|
*/
|
||||||
|
var passOptions = options.options || options;
|
||||||
|
var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
|
||||||
|
if (!getAnnotations) return;
|
||||||
|
if (options.async || getAnnotations.async) {
|
||||||
|
lintAsync(cm, getAnnotations, passOptions)
|
||||||
|
} else {
|
||||||
|
var annotations = getAnnotations(cm.getValue(), passOptions, cm);
|
||||||
|
if (!annotations) return;
|
||||||
|
if (annotations.then) annotations.then(function(issues) {
|
||||||
|
cm.operation(function() {updateLinting(cm, issues)})
|
||||||
|
});
|
||||||
|
else cm.operation(function() {updateLinting(cm, annotations)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLinting(cm, annotationsNotSorted) {
|
||||||
|
clearMarks(cm);
|
||||||
|
var state = cm.state.lint, options = state.options;
|
||||||
|
|
||||||
|
var annotations = groupByLine(annotationsNotSorted);
|
||||||
|
|
||||||
|
for (var line = 0; line < annotations.length; ++line) {
|
||||||
|
var anns = annotations[line];
|
||||||
|
if (!anns) continue;
|
||||||
|
|
||||||
|
var maxSeverity = null;
|
||||||
|
var tipLabel = state.hasGutter && document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (var i = 0; i < anns.length; ++i) {
|
||||||
|
var ann = anns[i];
|
||||||
|
var severity = ann.severity;
|
||||||
|
if (!severity) severity = "error";
|
||||||
|
maxSeverity = getMaxSeverity(maxSeverity, severity);
|
||||||
|
|
||||||
|
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
|
||||||
|
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
|
||||||
|
|
||||||
|
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
|
||||||
|
className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity,
|
||||||
|
__annotation: ann
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.hasGutter)
|
||||||
|
cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,
|
||||||
|
state.options.tooltips));
|
||||||
|
}
|
||||||
|
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(cm) {
|
||||||
|
var state = cm.state.lint;
|
||||||
|
if (!state) return;
|
||||||
|
clearTimeout(state.timeout);
|
||||||
|
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupTooltips(cm, annotations, e) {
|
||||||
|
var target = e.target || e.srcElement;
|
||||||
|
var tooltip = document.createDocumentFragment();
|
||||||
|
for (var i = 0; i < annotations.length; i++) {
|
||||||
|
var ann = annotations[i];
|
||||||
|
tooltip.appendChild(annotationTooltip(ann));
|
||||||
|
}
|
||||||
|
showTooltipFor(cm, e, tooltip, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseOver(cm, e) {
|
||||||
|
var target = e.target || e.srcElement;
|
||||||
|
if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
|
||||||
|
var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
|
||||||
|
var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
|
||||||
|
|
||||||
|
var annotations = [];
|
||||||
|
for (var i = 0; i < spans.length; ++i) {
|
||||||
|
var ann = spans[i].__annotation;
|
||||||
|
if (ann) annotations.push(ann);
|
||||||
|
}
|
||||||
|
if (annotations.length) popupTooltips(cm, annotations, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineOption("lint", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
clearMarks(cm);
|
||||||
|
if (cm.state.lint.options.lintOnChange !== false)
|
||||||
|
cm.off("change", onChange);
|
||||||
|
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
|
||||||
|
clearTimeout(cm.state.lint.timeout);
|
||||||
|
delete cm.state.lint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val) {
|
||||||
|
var gutters = cm.getOption("gutters"), hasLintGutter = false;
|
||||||
|
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
|
||||||
|
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
|
||||||
|
if (state.options.lintOnChange !== false)
|
||||||
|
cm.on("change", onChange);
|
||||||
|
if (state.options.tooltips != false && state.options.tooltips != "gutter")
|
||||||
|
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
|
||||||
|
|
||||||
|
startLinting(cm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("performLint", function() {
|
||||||
|
if (this.state.lint) startLinting(this);
|
||||||
|
});
|
||||||
|
});
|
||||||
41
src/modules/_CodeMirror/addon/lint/yaml-lint.js
Normal file
41
src/modules/_CodeMirror/addon/lint/yaml-lint.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Depends on js-yaml.js from https://github.com/nodeca/js-yaml
|
||||||
|
|
||||||
|
// declare global: jsyaml
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("lint", "yaml", function(text) {
|
||||||
|
var found = [];
|
||||||
|
if (!window.jsyaml) {
|
||||||
|
if (window.console) {
|
||||||
|
window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
try { jsyaml.loadAll(text); }
|
||||||
|
catch(e) {
|
||||||
|
var loc = e.mark,
|
||||||
|
// js-yaml YAMLException doesn't always provide an accurate lineno
|
||||||
|
// e.g., when there are multiple yaml docs
|
||||||
|
// ---
|
||||||
|
// ---
|
||||||
|
// foo:bar
|
||||||
|
from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),
|
||||||
|
to = from;
|
||||||
|
found.push({ from: from, to: to, message: e.message });
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
119
src/modules/_CodeMirror/addon/merge/merge.css
Normal file
119
src/modules/_CodeMirror/addon/merge/merge.css
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
.CodeMirror-merge {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
|
||||||
|
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
|
||||||
|
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
|
||||||
|
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-pane {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: normal;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-pane-rightmost {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-gap {
|
||||||
|
z-index: 2;
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
position: relative;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-scrolllock-wrap {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0; left: 50%;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock {
|
||||||
|
position: relative;
|
||||||
|
left: -50%;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock:after {
|
||||||
|
content: "\21db\00a0\00a0\21da";
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
|
||||||
|
content: "\21db\21da";
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0;
|
||||||
|
right: 0; bottom: 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copy {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #44c;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copy-reverse {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #44c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
|
||||||
|
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-chunk { background: #ffffe0; }
|
||||||
|
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
|
||||||
|
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
|
||||||
|
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-l-chunk { background: #eef; }
|
||||||
|
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
|
||||||
|
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
|
||||||
|
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
|
||||||
|
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
|
||||||
|
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-collapsed-widget:before {
|
||||||
|
content: "(...)";
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-collapsed-widget {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #88b;
|
||||||
|
background: #eef;
|
||||||
|
border: 1px solid #ddf;
|
||||||
|
font-size: 90%;
|
||||||
|
padding: 0 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
|
||||||
1006
src/modules/_CodeMirror/addon/merge/merge.js
Normal file
1006
src/modules/_CodeMirror/addon/merge/merge.js
Normal file
File diff suppressed because it is too large
Load Diff
66
src/modules/_CodeMirror/addon/mode/loadmode.js
Normal file
66
src/modules/_CodeMirror/addon/mode/loadmode.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), "cjs");
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror, "plain");
|
||||||
|
})(function(CodeMirror, env) {
|
||||||
|
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
|
||||||
|
|
||||||
|
var loading = {};
|
||||||
|
function splitCallback(cont, n) {
|
||||||
|
var countDown = n;
|
||||||
|
return function() { if (--countDown == 0) cont(); };
|
||||||
|
}
|
||||||
|
function ensureDeps(mode, cont, options) {
|
||||||
|
var modeObj = CodeMirror.modes[mode], deps = modeObj && modeObj.dependencies;
|
||||||
|
if (!deps) return cont();
|
||||||
|
var missing = [];
|
||||||
|
for (var i = 0; i < deps.length; ++i) {
|
||||||
|
if (!CodeMirror.modes.hasOwnProperty(deps[i]))
|
||||||
|
missing.push(deps[i]);
|
||||||
|
}
|
||||||
|
if (!missing.length) return cont();
|
||||||
|
var split = splitCallback(cont, missing.length);
|
||||||
|
for (var i = 0; i < missing.length; ++i)
|
||||||
|
CodeMirror.requireMode(missing[i], split, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.requireMode = function(mode, cont, options) {
|
||||||
|
if (typeof mode != "string") mode = mode.name;
|
||||||
|
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont, options);
|
||||||
|
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
|
||||||
|
|
||||||
|
var file = options && options.path ? options.path(mode) : CodeMirror.modeURL.replace(/%N/g, mode);
|
||||||
|
if (options && options.loadMode) {
|
||||||
|
options.loadMode(file, function() { ensureDeps(mode, cont, options) })
|
||||||
|
} else if (env == "plain") {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.src = file;
|
||||||
|
var others = document.getElementsByTagName("script")[0];
|
||||||
|
var list = loading[mode] = [cont];
|
||||||
|
CodeMirror.on(script, "load", function() {
|
||||||
|
ensureDeps(mode, function() {
|
||||||
|
for (var i = 0; i < list.length; ++i) list[i]();
|
||||||
|
}, options);
|
||||||
|
});
|
||||||
|
others.parentNode.insertBefore(script, others);
|
||||||
|
} else if (env == "cjs") {
|
||||||
|
require(file);
|
||||||
|
cont();
|
||||||
|
} else if (env == "amd") {
|
||||||
|
requirejs([file], cont);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.autoLoadMode = function(instance, mode, options) {
|
||||||
|
if (!CodeMirror.modes.hasOwnProperty(mode))
|
||||||
|
CodeMirror.requireMode(mode, function() {
|
||||||
|
instance.setOption("mode", instance.getOption("mode"));
|
||||||
|
}, options);
|
||||||
|
};
|
||||||
|
});
|
||||||
131
src/modules/_CodeMirror/addon/mode/multiplex.js
Normal file
131
src/modules/_CodeMirror/addon/mode/multiplex.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.multiplexingMode = function(outer /*, others */) {
|
||||||
|
// Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects
|
||||||
|
var others = Array.prototype.slice.call(arguments, 1);
|
||||||
|
|
||||||
|
function indexOf(string, pattern, from, returnEnd) {
|
||||||
|
if (typeof pattern == "string") {
|
||||||
|
var found = string.indexOf(pattern, from);
|
||||||
|
return returnEnd && found > -1 ? found + pattern.length : found;
|
||||||
|
}
|
||||||
|
var m = pattern.exec(from ? string.slice(from) : string);
|
||||||
|
return m ? m.index + from + (returnEnd ? m[0].length : 0) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startState: function() {
|
||||||
|
return {
|
||||||
|
outer: CodeMirror.startState(outer),
|
||||||
|
innerActive: null,
|
||||||
|
inner: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
copyState: function(state) {
|
||||||
|
return {
|
||||||
|
outer: CodeMirror.copyState(outer, state.outer),
|
||||||
|
innerActive: state.innerActive,
|
||||||
|
inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
token: function(stream, state) {
|
||||||
|
if (!state.innerActive) {
|
||||||
|
var cutOff = Infinity, oldContent = stream.string;
|
||||||
|
for (var i = 0; i < others.length; ++i) {
|
||||||
|
var other = others[i];
|
||||||
|
var found = indexOf(oldContent, other.open, stream.pos);
|
||||||
|
if (found == stream.pos) {
|
||||||
|
if (!other.parseDelimiters) stream.match(other.open);
|
||||||
|
state.innerActive = other;
|
||||||
|
|
||||||
|
// Get the outer indent, making sure to handle CodeMirror.Pass
|
||||||
|
var outerIndent = 0;
|
||||||
|
if (outer.indent) {
|
||||||
|
var possibleOuterIndent = outer.indent(state.outer, "", "");
|
||||||
|
if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.inner = CodeMirror.startState(other.mode, outerIndent);
|
||||||
|
return other.delimStyle && (other.delimStyle + " " + other.delimStyle + "-open");
|
||||||
|
} else if (found != -1 && found < cutOff) {
|
||||||
|
cutOff = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
|
||||||
|
var outerToken = outer.token(stream, state.outer);
|
||||||
|
if (cutOff != Infinity) stream.string = oldContent;
|
||||||
|
return outerToken;
|
||||||
|
} else {
|
||||||
|
var curInner = state.innerActive, oldContent = stream.string;
|
||||||
|
if (!curInner.close && stream.sol()) {
|
||||||
|
state.innerActive = state.inner = null;
|
||||||
|
return this.token(stream, state);
|
||||||
|
}
|
||||||
|
var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1;
|
||||||
|
if (found == stream.pos && !curInner.parseDelimiters) {
|
||||||
|
stream.match(curInner.close);
|
||||||
|
state.innerActive = state.inner = null;
|
||||||
|
return curInner.delimStyle && (curInner.delimStyle + " " + curInner.delimStyle + "-close");
|
||||||
|
}
|
||||||
|
if (found > -1) stream.string = oldContent.slice(0, found);
|
||||||
|
var innerToken = curInner.mode.token(stream, state.inner);
|
||||||
|
if (found > -1) stream.string = oldContent;
|
||||||
|
|
||||||
|
if (found == stream.pos && curInner.parseDelimiters)
|
||||||
|
state.innerActive = state.inner = null;
|
||||||
|
|
||||||
|
if (curInner.innerStyle) {
|
||||||
|
if (innerToken) innerToken = innerToken + " " + curInner.innerStyle;
|
||||||
|
else innerToken = curInner.innerStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerToken;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
indent: function(state, textAfter, line) {
|
||||||
|
var mode = state.innerActive ? state.innerActive.mode : outer;
|
||||||
|
if (!mode.indent) return CodeMirror.Pass;
|
||||||
|
return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line);
|
||||||
|
},
|
||||||
|
|
||||||
|
blankLine: function(state) {
|
||||||
|
var mode = state.innerActive ? state.innerActive.mode : outer;
|
||||||
|
if (mode.blankLine) {
|
||||||
|
mode.blankLine(state.innerActive ? state.inner : state.outer);
|
||||||
|
}
|
||||||
|
if (!state.innerActive) {
|
||||||
|
for (var i = 0; i < others.length; ++i) {
|
||||||
|
var other = others[i];
|
||||||
|
if (other.open === "\n") {
|
||||||
|
state.innerActive = other;
|
||||||
|
state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "", "") : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state.innerActive.close === "\n") {
|
||||||
|
state.innerActive = state.inner = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
electricChars: outer.electricChars,
|
||||||
|
|
||||||
|
innerMode: function(state) {
|
||||||
|
return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
33
src/modules/_CodeMirror/addon/mode/multiplex_test.js
Normal file
33
src/modules/_CodeMirror/addon/mode/multiplex_test.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
CodeMirror.defineMode("markdown_with_stex", function(){
|
||||||
|
var inner = CodeMirror.getMode({}, "stex");
|
||||||
|
var outer = CodeMirror.getMode({}, "markdown");
|
||||||
|
|
||||||
|
var innerOptions = {
|
||||||
|
open: '$',
|
||||||
|
close: '$',
|
||||||
|
mode: inner,
|
||||||
|
delimStyle: 'delim',
|
||||||
|
innerStyle: 'inner'
|
||||||
|
};
|
||||||
|
|
||||||
|
return CodeMirror.multiplexingMode(outer, innerOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
var mode = CodeMirror.getMode({}, "markdown_with_stex");
|
||||||
|
|
||||||
|
function MT(name) {
|
||||||
|
test.mode(
|
||||||
|
name,
|
||||||
|
mode,
|
||||||
|
Array.prototype.slice.call(arguments, 1),
|
||||||
|
'multiplexing');
|
||||||
|
}
|
||||||
|
|
||||||
|
MT(
|
||||||
|
"stexInsideMarkdown",
|
||||||
|
"[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]");
|
||||||
|
})();
|
||||||
90
src/modules/_CodeMirror/addon/mode/overlay.js
Normal file
90
src/modules/_CodeMirror/addon/mode/overlay.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Utility function that allows modes to be combined. The mode given
|
||||||
|
// as the base argument takes care of most of the normal mode
|
||||||
|
// functionality, but a second (typically simple) mode is used, which
|
||||||
|
// can override the style of text. Both modes get to parse all of the
|
||||||
|
// text, but when both assign a non-null style to a piece of code, the
|
||||||
|
// overlay wins, unless the combine argument was true and not overridden,
|
||||||
|
// or state.overlay.combineTokens was true, in which case the styles are
|
||||||
|
// combined.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.overlayMode = function(base, overlay, combine) {
|
||||||
|
return {
|
||||||
|
startState: function() {
|
||||||
|
return {
|
||||||
|
base: CodeMirror.startState(base),
|
||||||
|
overlay: CodeMirror.startState(overlay),
|
||||||
|
basePos: 0, baseCur: null,
|
||||||
|
overlayPos: 0, overlayCur: null,
|
||||||
|
streamSeen: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
copyState: function(state) {
|
||||||
|
return {
|
||||||
|
base: CodeMirror.copyState(base, state.base),
|
||||||
|
overlay: CodeMirror.copyState(overlay, state.overlay),
|
||||||
|
basePos: state.basePos, baseCur: null,
|
||||||
|
overlayPos: state.overlayPos, overlayCur: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
token: function(stream, state) {
|
||||||
|
if (stream != state.streamSeen ||
|
||||||
|
Math.min(state.basePos, state.overlayPos) < stream.start) {
|
||||||
|
state.streamSeen = stream;
|
||||||
|
state.basePos = state.overlayPos = stream.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.start == state.basePos) {
|
||||||
|
state.baseCur = base.token(stream, state.base);
|
||||||
|
state.basePos = stream.pos;
|
||||||
|
}
|
||||||
|
if (stream.start == state.overlayPos) {
|
||||||
|
stream.pos = stream.start;
|
||||||
|
state.overlayCur = overlay.token(stream, state.overlay);
|
||||||
|
state.overlayPos = stream.pos;
|
||||||
|
}
|
||||||
|
stream.pos = Math.min(state.basePos, state.overlayPos);
|
||||||
|
|
||||||
|
// state.overlay.combineTokens always takes precedence over combine,
|
||||||
|
// unless set to null
|
||||||
|
if (state.overlayCur == null) return state.baseCur;
|
||||||
|
else if (state.baseCur != null &&
|
||||||
|
state.overlay.combineTokens ||
|
||||||
|
combine && state.overlay.combineTokens == null)
|
||||||
|
return state.baseCur + " " + state.overlayCur;
|
||||||
|
else return state.overlayCur;
|
||||||
|
},
|
||||||
|
|
||||||
|
indent: base.indent && function(state, textAfter, line) {
|
||||||
|
return base.indent(state.base, textAfter, line);
|
||||||
|
},
|
||||||
|
electricChars: base.electricChars,
|
||||||
|
|
||||||
|
innerMode: function(state) { return {state: state.base, mode: base}; },
|
||||||
|
|
||||||
|
blankLine: function(state) {
|
||||||
|
var baseToken, overlayToken;
|
||||||
|
if (base.blankLine) baseToken = base.blankLine(state.base);
|
||||||
|
if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);
|
||||||
|
|
||||||
|
return overlayToken == null ?
|
||||||
|
baseToken :
|
||||||
|
(combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
216
src/modules/_CodeMirror/addon/mode/simple.js
Normal file
216
src/modules/_CodeMirror/addon/mode/simple.js
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineSimpleMode = function(name, states) {
|
||||||
|
CodeMirror.defineMode(name, function(config) {
|
||||||
|
return CodeMirror.simpleMode(config, states);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.simpleMode = function(config, states) {
|
||||||
|
ensureState(states, "start");
|
||||||
|
var states_ = {}, meta = states.meta || {}, hasIndentation = false;
|
||||||
|
for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
|
||||||
|
var list = states_[state] = [], orig = states[state];
|
||||||
|
for (var i = 0; i < orig.length; i++) {
|
||||||
|
var data = orig[i];
|
||||||
|
list.push(new Rule(data, states));
|
||||||
|
if (data.indent || data.dedent) hasIndentation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mode = {
|
||||||
|
startState: function() {
|
||||||
|
return {state: "start", pending: null,
|
||||||
|
local: null, localState: null,
|
||||||
|
indent: hasIndentation ? [] : null};
|
||||||
|
},
|
||||||
|
copyState: function(state) {
|
||||||
|
var s = {state: state.state, pending: state.pending,
|
||||||
|
local: state.local, localState: null,
|
||||||
|
indent: state.indent && state.indent.slice(0)};
|
||||||
|
if (state.localState)
|
||||||
|
s.localState = CodeMirror.copyState(state.local.mode, state.localState);
|
||||||
|
if (state.stack)
|
||||||
|
s.stack = state.stack.slice(0);
|
||||||
|
for (var pers = state.persistentStates; pers; pers = pers.next)
|
||||||
|
s.persistentStates = {mode: pers.mode,
|
||||||
|
spec: pers.spec,
|
||||||
|
state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
|
||||||
|
next: s.persistentStates};
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
token: tokenFunction(states_, config),
|
||||||
|
innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
|
||||||
|
indent: indentFunction(states_, meta)
|
||||||
|
};
|
||||||
|
if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
|
||||||
|
mode[prop] = meta[prop];
|
||||||
|
return mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ensureState(states, name) {
|
||||||
|
if (!states.hasOwnProperty(name))
|
||||||
|
throw new Error("Undefined state " + name + " in simple mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toRegex(val, caret) {
|
||||||
|
if (!val) return /(?:)/;
|
||||||
|
var flags = "";
|
||||||
|
if (val instanceof RegExp) {
|
||||||
|
if (val.ignoreCase) flags = "i";
|
||||||
|
val = val.source;
|
||||||
|
} else {
|
||||||
|
val = String(val);
|
||||||
|
}
|
||||||
|
return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function asToken(val) {
|
||||||
|
if (!val) return null;
|
||||||
|
if (val.apply) return val
|
||||||
|
if (typeof val == "string") return val.replace(/\./g, " ");
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < val.length; i++)
|
||||||
|
result.push(val[i] && val[i].replace(/\./g, " "));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rule(data, states) {
|
||||||
|
if (data.next || data.push) ensureState(states, data.next || data.push);
|
||||||
|
this.regex = toRegex(data.regex);
|
||||||
|
this.token = asToken(data.token);
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenFunction(states, config) {
|
||||||
|
return function(stream, state) {
|
||||||
|
if (state.pending) {
|
||||||
|
var pend = state.pending.shift();
|
||||||
|
if (state.pending.length == 0) state.pending = null;
|
||||||
|
stream.pos += pend.text.length;
|
||||||
|
return pend.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.local) {
|
||||||
|
if (state.local.end && stream.match(state.local.end)) {
|
||||||
|
var tok = state.local.endToken || null;
|
||||||
|
state.local = state.localState = null;
|
||||||
|
return tok;
|
||||||
|
} else {
|
||||||
|
var tok = state.local.mode.token(stream, state.localState), m;
|
||||||
|
if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
|
||||||
|
stream.pos = stream.start + m.index;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var curState = states[state.state];
|
||||||
|
for (var i = 0; i < curState.length; i++) {
|
||||||
|
var rule = curState[i];
|
||||||
|
var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
|
||||||
|
if (matches) {
|
||||||
|
if (rule.data.next) {
|
||||||
|
state.state = rule.data.next;
|
||||||
|
} else if (rule.data.push) {
|
||||||
|
(state.stack || (state.stack = [])).push(state.state);
|
||||||
|
state.state = rule.data.push;
|
||||||
|
} else if (rule.data.pop && state.stack && state.stack.length) {
|
||||||
|
state.state = state.stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.data.mode)
|
||||||
|
enterLocalMode(config, state, rule.data.mode, rule.token);
|
||||||
|
if (rule.data.indent)
|
||||||
|
state.indent.push(stream.indentation() + config.indentUnit);
|
||||||
|
if (rule.data.dedent)
|
||||||
|
state.indent.pop();
|
||||||
|
var token = rule.token
|
||||||
|
if (token && token.apply) token = token(matches)
|
||||||
|
if (matches.length > 2 && rule.token && typeof rule.token != "string") {
|
||||||
|
state.pending = [];
|
||||||
|
for (var j = 2; j < matches.length; j++)
|
||||||
|
if (matches[j])
|
||||||
|
state.pending.push({text: matches[j], token: rule.token[j - 1]});
|
||||||
|
stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
|
||||||
|
return token[0];
|
||||||
|
} else if (token && token.join) {
|
||||||
|
return token[0];
|
||||||
|
} else {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.next();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmp(a, b) {
|
||||||
|
if (a === b) return true;
|
||||||
|
if (!a || typeof a != "object" || !b || typeof b != "object") return false;
|
||||||
|
var props = 0;
|
||||||
|
for (var prop in a) if (a.hasOwnProperty(prop)) {
|
||||||
|
if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
|
||||||
|
props++;
|
||||||
|
}
|
||||||
|
for (var prop in b) if (b.hasOwnProperty(prop)) props--;
|
||||||
|
return props == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enterLocalMode(config, state, spec, token) {
|
||||||
|
var pers;
|
||||||
|
if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
|
||||||
|
if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
|
||||||
|
var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
|
||||||
|
var lState = pers ? pers.state : CodeMirror.startState(mode);
|
||||||
|
if (spec.persistent && !pers)
|
||||||
|
state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};
|
||||||
|
|
||||||
|
state.localState = lState;
|
||||||
|
state.local = {mode: mode,
|
||||||
|
end: spec.end && toRegex(spec.end),
|
||||||
|
endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
|
||||||
|
endToken: token && token.join ? token[token.length - 1] : token};
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexOf(val, arr) {
|
||||||
|
for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function indentFunction(states, meta) {
|
||||||
|
return function(state, textAfter, line) {
|
||||||
|
if (state.local && state.local.mode.indent)
|
||||||
|
return state.local.mode.indent(state.localState, textAfter, line);
|
||||||
|
if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
|
||||||
|
var pos = state.indent.length - 1, rules = states[state.state];
|
||||||
|
scan: for (;;) {
|
||||||
|
for (var i = 0; i < rules.length; i++) {
|
||||||
|
var rule = rules[i];
|
||||||
|
if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
|
||||||
|
var m = rule.regex.exec(textAfter);
|
||||||
|
if (m && m[0]) {
|
||||||
|
pos--;
|
||||||
|
if (rule.next || rule.push) rules = states[rule.next || rule.push];
|
||||||
|
textAfter = textAfter.slice(m[0].length);
|
||||||
|
continue scan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pos < 0 ? 0 : state.indent[pos];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
40
src/modules/_CodeMirror/addon/runmode/colorize.js
Normal file
40
src/modules/_CodeMirror/addon/runmode/colorize.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./runmode"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./runmode"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
|
||||||
|
|
||||||
|
function textContent(node, out) {
|
||||||
|
if (node.nodeType == 3) return out.push(node.nodeValue);
|
||||||
|
for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
|
||||||
|
textContent(ch, out);
|
||||||
|
if (isBlock.test(node.nodeType)) out.push("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.colorize = function(collection, defaultMode) {
|
||||||
|
if (!collection) collection = document.body.getElementsByTagName("pre");
|
||||||
|
|
||||||
|
for (var i = 0; i < collection.length; ++i) {
|
||||||
|
var node = collection[i];
|
||||||
|
var mode = node.getAttribute("data-lang") || defaultMode;
|
||||||
|
if (!mode) continue;
|
||||||
|
|
||||||
|
var text = [];
|
||||||
|
textContent(node, text);
|
||||||
|
node.innerHTML = "";
|
||||||
|
CodeMirror.runMode(text.join(""), mode, node);
|
||||||
|
|
||||||
|
node.className += " cm-s-default";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
333
src/modules/_CodeMirror/addon/runmode/runmode-standalone.js
Normal file
333
src/modules/_CodeMirror/addon/runmode/runmode-standalone.js
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function copyObj(obj, target, overwrite) {
|
||||||
|
if (!target) { target = {}; }
|
||||||
|
for (var prop in obj)
|
||||||
|
{ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
|
||||||
|
{ target[prop] = obj[prop]; } }
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counts the column offset in a string, taking tabs into account.
|
||||||
|
// Used mostly to find indentation.
|
||||||
|
function countColumn(string, end, tabSize, startIndex, startValue) {
|
||||||
|
if (end == null) {
|
||||||
|
end = string.search(/[^\s\u00a0]/);
|
||||||
|
if (end == -1) { end = string.length; }
|
||||||
|
}
|
||||||
|
for (var i = startIndex || 0, n = startValue || 0;;) {
|
||||||
|
var nextTab = string.indexOf("\t", i);
|
||||||
|
if (nextTab < 0 || nextTab >= end)
|
||||||
|
{ return n + (end - i) }
|
||||||
|
n += nextTab - i;
|
||||||
|
n += tabSize - (n % tabSize);
|
||||||
|
i = nextTab + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nothing() {}
|
||||||
|
|
||||||
|
function createObj(base, props) {
|
||||||
|
var inst;
|
||||||
|
if (Object.create) {
|
||||||
|
inst = Object.create(base);
|
||||||
|
} else {
|
||||||
|
nothing.prototype = base;
|
||||||
|
inst = new nothing();
|
||||||
|
}
|
||||||
|
if (props) { copyObj(props, inst); }
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
// STRING STREAM
|
||||||
|
|
||||||
|
// Fed to the mode parsers, provides helper functions to make
|
||||||
|
// parsers more succinct.
|
||||||
|
|
||||||
|
var StringStream = function(string, tabSize, lineOracle) {
|
||||||
|
this.pos = this.start = 0;
|
||||||
|
this.string = string;
|
||||||
|
this.tabSize = tabSize || 8;
|
||||||
|
this.lastColumnPos = this.lastColumnValue = 0;
|
||||||
|
this.lineStart = 0;
|
||||||
|
this.lineOracle = lineOracle;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringStream.prototype.eol = function () {return this.pos >= this.string.length};
|
||||||
|
StringStream.prototype.sol = function () {return this.pos == this.lineStart};
|
||||||
|
StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
|
||||||
|
StringStream.prototype.next = function () {
|
||||||
|
if (this.pos < this.string.length)
|
||||||
|
{ return this.string.charAt(this.pos++) }
|
||||||
|
};
|
||||||
|
StringStream.prototype.eat = function (match) {
|
||||||
|
var ch = this.string.charAt(this.pos);
|
||||||
|
var ok;
|
||||||
|
if (typeof match == "string") { ok = ch == match; }
|
||||||
|
else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
|
||||||
|
if (ok) {++this.pos; return ch}
|
||||||
|
};
|
||||||
|
StringStream.prototype.eatWhile = function (match) {
|
||||||
|
var start = this.pos;
|
||||||
|
while (this.eat(match)){}
|
||||||
|
return this.pos > start
|
||||||
|
};
|
||||||
|
StringStream.prototype.eatSpace = function () {
|
||||||
|
var start = this.pos;
|
||||||
|
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
|
||||||
|
return this.pos > start
|
||||||
|
};
|
||||||
|
StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
|
||||||
|
StringStream.prototype.skipTo = function (ch) {
|
||||||
|
var found = this.string.indexOf(ch, this.pos);
|
||||||
|
if (found > -1) {this.pos = found; return true}
|
||||||
|
};
|
||||||
|
StringStream.prototype.backUp = function (n) {this.pos -= n;};
|
||||||
|
StringStream.prototype.column = function () {
|
||||||
|
if (this.lastColumnPos < this.start) {
|
||||||
|
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
||||||
|
this.lastColumnPos = this.start;
|
||||||
|
}
|
||||||
|
return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
|
||||||
|
};
|
||||||
|
StringStream.prototype.indentation = function () {
|
||||||
|
return countColumn(this.string, null, this.tabSize) -
|
||||||
|
(this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
|
||||||
|
};
|
||||||
|
StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
|
||||||
|
if (typeof pattern == "string") {
|
||||||
|
var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
|
||||||
|
var substr = this.string.substr(this.pos, pattern.length);
|
||||||
|
if (cased(substr) == cased(pattern)) {
|
||||||
|
if (consume !== false) { this.pos += pattern.length; }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var match = this.string.slice(this.pos).match(pattern);
|
||||||
|
if (match && match.index > 0) { return null }
|
||||||
|
if (match && consume !== false) { this.pos += match[0].length; }
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
};
|
||||||
|
StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
|
||||||
|
StringStream.prototype.hideFirstChars = function (n, inner) {
|
||||||
|
this.lineStart += n;
|
||||||
|
try { return inner() }
|
||||||
|
finally { this.lineStart -= n; }
|
||||||
|
};
|
||||||
|
StringStream.prototype.lookAhead = function (n) {
|
||||||
|
var oracle = this.lineOracle;
|
||||||
|
return oracle && oracle.lookAhead(n)
|
||||||
|
};
|
||||||
|
StringStream.prototype.baseToken = function () {
|
||||||
|
var oracle = this.lineOracle;
|
||||||
|
return oracle && oracle.baseToken(this.pos)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Known modes, by name and by MIME
|
||||||
|
var modes = {}, mimeModes = {};
|
||||||
|
|
||||||
|
// Extra arguments are stored as the mode's dependencies, which is
|
||||||
|
// used by (legacy) mechanisms like loadmode.js to automatically
|
||||||
|
// load a mode. (Preferred mechanism is the require/define calls.)
|
||||||
|
function defineMode(name, mode) {
|
||||||
|
if (arguments.length > 2)
|
||||||
|
{ mode.dependencies = Array.prototype.slice.call(arguments, 2); }
|
||||||
|
modes[name] = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineMIME(mime, spec) {
|
||||||
|
mimeModes[mime] = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a MIME type, a {name, ...options} config object, or a name
|
||||||
|
// string, return a mode config object.
|
||||||
|
function resolveMode(spec) {
|
||||||
|
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
|
||||||
|
spec = mimeModes[spec];
|
||||||
|
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
|
||||||
|
var found = mimeModes[spec.name];
|
||||||
|
if (typeof found == "string") { found = {name: found}; }
|
||||||
|
spec = createObj(found, spec);
|
||||||
|
spec.name = found.name;
|
||||||
|
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
|
||||||
|
return resolveMode("application/xml")
|
||||||
|
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
|
||||||
|
return resolveMode("application/json")
|
||||||
|
}
|
||||||
|
if (typeof spec == "string") { return {name: spec} }
|
||||||
|
else { return spec || {name: "null"} }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a mode spec (anything that resolveMode accepts), find and
|
||||||
|
// initialize an actual mode object.
|
||||||
|
function getMode(options, spec) {
|
||||||
|
spec = resolveMode(spec);
|
||||||
|
var mfactory = modes[spec.name];
|
||||||
|
if (!mfactory) { return getMode(options, "text/plain") }
|
||||||
|
var modeObj = mfactory(options, spec);
|
||||||
|
if (modeExtensions.hasOwnProperty(spec.name)) {
|
||||||
|
var exts = modeExtensions[spec.name];
|
||||||
|
for (var prop in exts) {
|
||||||
|
if (!exts.hasOwnProperty(prop)) { continue }
|
||||||
|
if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
|
||||||
|
modeObj[prop] = exts[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modeObj.name = spec.name;
|
||||||
|
if (spec.helperType) { modeObj.helperType = spec.helperType; }
|
||||||
|
if (spec.modeProps) { for (var prop$1 in spec.modeProps)
|
||||||
|
{ modeObj[prop$1] = spec.modeProps[prop$1]; } }
|
||||||
|
|
||||||
|
return modeObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be used to attach properties to mode objects from
|
||||||
|
// outside the actual mode definition.
|
||||||
|
var modeExtensions = {};
|
||||||
|
function extendMode(mode, properties) {
|
||||||
|
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
||||||
|
copyObj(properties, exts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyState(mode, state) {
|
||||||
|
if (state === true) { return state }
|
||||||
|
if (mode.copyState) { return mode.copyState(state) }
|
||||||
|
var nstate = {};
|
||||||
|
for (var n in state) {
|
||||||
|
var val = state[n];
|
||||||
|
if (val instanceof Array) { val = val.concat([]); }
|
||||||
|
nstate[n] = val;
|
||||||
|
}
|
||||||
|
return nstate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a mode and a state (for that mode), find the inner mode and
|
||||||
|
// state at the position that the state refers to.
|
||||||
|
function innerMode(mode, state) {
|
||||||
|
var info;
|
||||||
|
while (mode.innerMode) {
|
||||||
|
info = mode.innerMode(state);
|
||||||
|
if (!info || info.mode == mode) { break }
|
||||||
|
state = info.state;
|
||||||
|
mode = info.mode;
|
||||||
|
}
|
||||||
|
return info || {mode: mode, state: state}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startState(mode, a1, a2) {
|
||||||
|
return mode.startState ? mode.startState(a1, a2) : true
|
||||||
|
}
|
||||||
|
|
||||||
|
var modeMethods = {
|
||||||
|
__proto__: null,
|
||||||
|
modes: modes,
|
||||||
|
mimeModes: mimeModes,
|
||||||
|
defineMode: defineMode,
|
||||||
|
defineMIME: defineMIME,
|
||||||
|
resolveMode: resolveMode,
|
||||||
|
getMode: getMode,
|
||||||
|
modeExtensions: modeExtensions,
|
||||||
|
extendMode: extendMode,
|
||||||
|
copyState: copyState,
|
||||||
|
innerMode: innerMode,
|
||||||
|
startState: startState
|
||||||
|
};
|
||||||
|
|
||||||
|
// declare global: globalThis, CodeMirror
|
||||||
|
|
||||||
|
// Create a minimal CodeMirror needed to use runMode, and assign to root.
|
||||||
|
var root = typeof globalThis !== 'undefined' ? globalThis : window;
|
||||||
|
root.CodeMirror = {};
|
||||||
|
|
||||||
|
// Copy StringStream and mode methods into CodeMirror object.
|
||||||
|
CodeMirror.StringStream = StringStream;
|
||||||
|
for (var exported in modeMethods) { CodeMirror[exported] = modeMethods[exported]; }
|
||||||
|
|
||||||
|
// Minimal default mode.
|
||||||
|
CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
|
||||||
|
CodeMirror.defineMIME("text/plain", "null");
|
||||||
|
|
||||||
|
CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min;
|
||||||
|
CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) };
|
||||||
|
|
||||||
|
CodeMirror.defaults = { indentUnit: 2 };
|
||||||
|
|
||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
{ mod(require("../../lib/codemirror")); }
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
{ define(["../../lib/codemirror"], mod); }
|
||||||
|
else // Plain browser env
|
||||||
|
{ mod(CodeMirror); }
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
|
||||||
|
CodeMirror.runMode = function(string, modespec, callback, options) {
|
||||||
|
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
|
||||||
|
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
|
||||||
|
|
||||||
|
// Create a tokenizing callback function if passed-in callback is a DOM element.
|
||||||
|
if (callback.appendChild) {
|
||||||
|
var ie = /MSIE \d/.test(navigator.userAgent);
|
||||||
|
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
||||||
|
var node = callback, col = 0;
|
||||||
|
node.innerHTML = "";
|
||||||
|
callback = function(text, style) {
|
||||||
|
if (text == "\n") {
|
||||||
|
// Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
|
||||||
|
// Emitting a carriage return makes everything ok.
|
||||||
|
node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
|
||||||
|
col = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var content = "";
|
||||||
|
// replace tabs
|
||||||
|
for (var pos = 0;;) {
|
||||||
|
var idx = text.indexOf("\t", pos);
|
||||||
|
if (idx == -1) {
|
||||||
|
content += text.slice(pos);
|
||||||
|
col += text.length - pos;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
col += idx - pos;
|
||||||
|
content += text.slice(pos, idx);
|
||||||
|
var size = tabSize - col % tabSize;
|
||||||
|
col += size;
|
||||||
|
for (var i = 0; i < size; ++i) { content += " "; }
|
||||||
|
pos = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a node with token style and append it to the callback DOM element.
|
||||||
|
if (style) {
|
||||||
|
var sp = node.appendChild(document.createElement("span"));
|
||||||
|
sp.className = "cm-" + style.replace(/ +/g, " cm-");
|
||||||
|
sp.appendChild(document.createTextNode(content));
|
||||||
|
} else {
|
||||||
|
node.appendChild(document.createTextNode(content));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
|
||||||
|
for (var i = 0, e = lines.length; i < e; ++i) {
|
||||||
|
if (i) { callback("\n"); }
|
||||||
|
var stream = new CodeMirror.StringStream(lines[i], null, {
|
||||||
|
lookAhead: function(n) { return lines[i + n] },
|
||||||
|
baseToken: function() {}
|
||||||
|
});
|
||||||
|
if (!stream.string && mode.blankLine) { mode.blankLine(state); }
|
||||||
|
while (!stream.eol()) {
|
||||||
|
var style = mode.token(stream, state);
|
||||||
|
callback(stream.current(), style, i, stream.start, state);
|
||||||
|
stream.start = stream.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
||||||
76
src/modules/_CodeMirror/addon/runmode/runmode.js
Normal file
76
src/modules/_CodeMirror/addon/runmode/runmode.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.runMode = function(string, modespec, callback, options) {
|
||||||
|
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
|
||||||
|
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
|
||||||
|
|
||||||
|
// Create a tokenizing callback function if passed-in callback is a DOM element.
|
||||||
|
if (callback.appendChild) {
|
||||||
|
var ie = /MSIE \d/.test(navigator.userAgent);
|
||||||
|
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
||||||
|
var node = callback, col = 0;
|
||||||
|
node.innerHTML = "";
|
||||||
|
callback = function(text, style) {
|
||||||
|
if (text == "\n") {
|
||||||
|
// Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
|
||||||
|
// Emitting a carriage return makes everything ok.
|
||||||
|
node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
|
||||||
|
col = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var content = "";
|
||||||
|
// replace tabs
|
||||||
|
for (var pos = 0;;) {
|
||||||
|
var idx = text.indexOf("\t", pos);
|
||||||
|
if (idx == -1) {
|
||||||
|
content += text.slice(pos);
|
||||||
|
col += text.length - pos;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
col += idx - pos;
|
||||||
|
content += text.slice(pos, idx);
|
||||||
|
var size = tabSize - col % tabSize;
|
||||||
|
col += size;
|
||||||
|
for (var i = 0; i < size; ++i) content += " ";
|
||||||
|
pos = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a node with token style and append it to the callback DOM element.
|
||||||
|
if (style) {
|
||||||
|
var sp = node.appendChild(document.createElement("span"));
|
||||||
|
sp.className = "cm-" + style.replace(/ +/g, " cm-");
|
||||||
|
sp.appendChild(document.createTextNode(content));
|
||||||
|
} else {
|
||||||
|
node.appendChild(document.createTextNode(content));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
|
||||||
|
for (var i = 0, e = lines.length; i < e; ++i) {
|
||||||
|
if (i) callback("\n");
|
||||||
|
var stream = new CodeMirror.StringStream(lines[i], null, {
|
||||||
|
lookAhead: function(n) { return lines[i + n] },
|
||||||
|
baseToken: function() {}
|
||||||
|
});
|
||||||
|
if (!stream.string && mode.blankLine) mode.blankLine(state);
|
||||||
|
while (!stream.eol()) {
|
||||||
|
var style = mode.token(stream, state);
|
||||||
|
callback(stream.current(), style, i, stream.start, state);
|
||||||
|
stream.start = stream.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
329
src/modules/_CodeMirror/addon/runmode/runmode.node.js
Normal file
329
src/modules/_CodeMirror/addon/runmode/runmode.node.js
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function copyObj(obj, target, overwrite) {
|
||||||
|
if (!target) { target = {}; }
|
||||||
|
for (var prop in obj)
|
||||||
|
{ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
|
||||||
|
{ target[prop] = obj[prop]; } }
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counts the column offset in a string, taking tabs into account.
|
||||||
|
// Used mostly to find indentation.
|
||||||
|
function countColumn(string, end, tabSize, startIndex, startValue) {
|
||||||
|
if (end == null) {
|
||||||
|
end = string.search(/[^\s\u00a0]/);
|
||||||
|
if (end == -1) { end = string.length; }
|
||||||
|
}
|
||||||
|
for (var i = startIndex || 0, n = startValue || 0;;) {
|
||||||
|
var nextTab = string.indexOf("\t", i);
|
||||||
|
if (nextTab < 0 || nextTab >= end)
|
||||||
|
{ return n + (end - i) }
|
||||||
|
n += nextTab - i;
|
||||||
|
n += tabSize - (n % tabSize);
|
||||||
|
i = nextTab + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nothing() {}
|
||||||
|
|
||||||
|
function createObj(base, props) {
|
||||||
|
var inst;
|
||||||
|
if (Object.create) {
|
||||||
|
inst = Object.create(base);
|
||||||
|
} else {
|
||||||
|
nothing.prototype = base;
|
||||||
|
inst = new nothing();
|
||||||
|
}
|
||||||
|
if (props) { copyObj(props, inst); }
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
// STRING STREAM
|
||||||
|
|
||||||
|
// Fed to the mode parsers, provides helper functions to make
|
||||||
|
// parsers more succinct.
|
||||||
|
|
||||||
|
var StringStream = function(string, tabSize, lineOracle) {
|
||||||
|
this.pos = this.start = 0;
|
||||||
|
this.string = string;
|
||||||
|
this.tabSize = tabSize || 8;
|
||||||
|
this.lastColumnPos = this.lastColumnValue = 0;
|
||||||
|
this.lineStart = 0;
|
||||||
|
this.lineOracle = lineOracle;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringStream.prototype.eol = function () {return this.pos >= this.string.length};
|
||||||
|
StringStream.prototype.sol = function () {return this.pos == this.lineStart};
|
||||||
|
StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
|
||||||
|
StringStream.prototype.next = function () {
|
||||||
|
if (this.pos < this.string.length)
|
||||||
|
{ return this.string.charAt(this.pos++) }
|
||||||
|
};
|
||||||
|
StringStream.prototype.eat = function (match) {
|
||||||
|
var ch = this.string.charAt(this.pos);
|
||||||
|
var ok;
|
||||||
|
if (typeof match == "string") { ok = ch == match; }
|
||||||
|
else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
|
||||||
|
if (ok) {++this.pos; return ch}
|
||||||
|
};
|
||||||
|
StringStream.prototype.eatWhile = function (match) {
|
||||||
|
var start = this.pos;
|
||||||
|
while (this.eat(match)){}
|
||||||
|
return this.pos > start
|
||||||
|
};
|
||||||
|
StringStream.prototype.eatSpace = function () {
|
||||||
|
var start = this.pos;
|
||||||
|
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
|
||||||
|
return this.pos > start
|
||||||
|
};
|
||||||
|
StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
|
||||||
|
StringStream.prototype.skipTo = function (ch) {
|
||||||
|
var found = this.string.indexOf(ch, this.pos);
|
||||||
|
if (found > -1) {this.pos = found; return true}
|
||||||
|
};
|
||||||
|
StringStream.prototype.backUp = function (n) {this.pos -= n;};
|
||||||
|
StringStream.prototype.column = function () {
|
||||||
|
if (this.lastColumnPos < this.start) {
|
||||||
|
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
||||||
|
this.lastColumnPos = this.start;
|
||||||
|
}
|
||||||
|
return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
|
||||||
|
};
|
||||||
|
StringStream.prototype.indentation = function () {
|
||||||
|
return countColumn(this.string, null, this.tabSize) -
|
||||||
|
(this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
|
||||||
|
};
|
||||||
|
StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
|
||||||
|
if (typeof pattern == "string") {
|
||||||
|
var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
|
||||||
|
var substr = this.string.substr(this.pos, pattern.length);
|
||||||
|
if (cased(substr) == cased(pattern)) {
|
||||||
|
if (consume !== false) { this.pos += pattern.length; }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var match = this.string.slice(this.pos).match(pattern);
|
||||||
|
if (match && match.index > 0) { return null }
|
||||||
|
if (match && consume !== false) { this.pos += match[0].length; }
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
};
|
||||||
|
StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
|
||||||
|
StringStream.prototype.hideFirstChars = function (n, inner) {
|
||||||
|
this.lineStart += n;
|
||||||
|
try { return inner() }
|
||||||
|
finally { this.lineStart -= n; }
|
||||||
|
};
|
||||||
|
StringStream.prototype.lookAhead = function (n) {
|
||||||
|
var oracle = this.lineOracle;
|
||||||
|
return oracle && oracle.lookAhead(n)
|
||||||
|
};
|
||||||
|
StringStream.prototype.baseToken = function () {
|
||||||
|
var oracle = this.lineOracle;
|
||||||
|
return oracle && oracle.baseToken(this.pos)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Known modes, by name and by MIME
|
||||||
|
var modes = {}, mimeModes = {};
|
||||||
|
|
||||||
|
// Extra arguments are stored as the mode's dependencies, which is
|
||||||
|
// used by (legacy) mechanisms like loadmode.js to automatically
|
||||||
|
// load a mode. (Preferred mechanism is the require/define calls.)
|
||||||
|
function defineMode(name, mode) {
|
||||||
|
if (arguments.length > 2)
|
||||||
|
{ mode.dependencies = Array.prototype.slice.call(arguments, 2); }
|
||||||
|
modes[name] = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineMIME(mime, spec) {
|
||||||
|
mimeModes[mime] = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a MIME type, a {name, ...options} config object, or a name
|
||||||
|
// string, return a mode config object.
|
||||||
|
function resolveMode(spec) {
|
||||||
|
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
|
||||||
|
spec = mimeModes[spec];
|
||||||
|
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
|
||||||
|
var found = mimeModes[spec.name];
|
||||||
|
if (typeof found == "string") { found = {name: found}; }
|
||||||
|
spec = createObj(found, spec);
|
||||||
|
spec.name = found.name;
|
||||||
|
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
|
||||||
|
return resolveMode("application/xml")
|
||||||
|
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
|
||||||
|
return resolveMode("application/json")
|
||||||
|
}
|
||||||
|
if (typeof spec == "string") { return {name: spec} }
|
||||||
|
else { return spec || {name: "null"} }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a mode spec (anything that resolveMode accepts), find and
|
||||||
|
// initialize an actual mode object.
|
||||||
|
function getMode(options, spec) {
|
||||||
|
spec = resolveMode(spec);
|
||||||
|
var mfactory = modes[spec.name];
|
||||||
|
if (!mfactory) { return getMode(options, "text/plain") }
|
||||||
|
var modeObj = mfactory(options, spec);
|
||||||
|
if (modeExtensions.hasOwnProperty(spec.name)) {
|
||||||
|
var exts = modeExtensions[spec.name];
|
||||||
|
for (var prop in exts) {
|
||||||
|
if (!exts.hasOwnProperty(prop)) { continue }
|
||||||
|
if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
|
||||||
|
modeObj[prop] = exts[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modeObj.name = spec.name;
|
||||||
|
if (spec.helperType) { modeObj.helperType = spec.helperType; }
|
||||||
|
if (spec.modeProps) { for (var prop$1 in spec.modeProps)
|
||||||
|
{ modeObj[prop$1] = spec.modeProps[prop$1]; } }
|
||||||
|
|
||||||
|
return modeObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be used to attach properties to mode objects from
|
||||||
|
// outside the actual mode definition.
|
||||||
|
var modeExtensions = {};
|
||||||
|
function extendMode(mode, properties) {
|
||||||
|
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
||||||
|
copyObj(properties, exts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyState(mode, state) {
|
||||||
|
if (state === true) { return state }
|
||||||
|
if (mode.copyState) { return mode.copyState(state) }
|
||||||
|
var nstate = {};
|
||||||
|
for (var n in state) {
|
||||||
|
var val = state[n];
|
||||||
|
if (val instanceof Array) { val = val.concat([]); }
|
||||||
|
nstate[n] = val;
|
||||||
|
}
|
||||||
|
return nstate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a mode and a state (for that mode), find the inner mode and
|
||||||
|
// state at the position that the state refers to.
|
||||||
|
function innerMode(mode, state) {
|
||||||
|
var info;
|
||||||
|
while (mode.innerMode) {
|
||||||
|
info = mode.innerMode(state);
|
||||||
|
if (!info || info.mode == mode) { break }
|
||||||
|
state = info.state;
|
||||||
|
mode = info.mode;
|
||||||
|
}
|
||||||
|
return info || {mode: mode, state: state}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startState(mode, a1, a2) {
|
||||||
|
return mode.startState ? mode.startState(a1, a2) : true
|
||||||
|
}
|
||||||
|
|
||||||
|
var modeMethods = {
|
||||||
|
__proto__: null,
|
||||||
|
modes: modes,
|
||||||
|
mimeModes: mimeModes,
|
||||||
|
defineMode: defineMode,
|
||||||
|
defineMIME: defineMIME,
|
||||||
|
resolveMode: resolveMode,
|
||||||
|
getMode: getMode,
|
||||||
|
modeExtensions: modeExtensions,
|
||||||
|
extendMode: extendMode,
|
||||||
|
copyState: copyState,
|
||||||
|
innerMode: innerMode,
|
||||||
|
startState: startState
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy StringStream and mode methods into exports (CodeMirror) object.
|
||||||
|
exports.StringStream = StringStream;
|
||||||
|
exports.countColumn = countColumn;
|
||||||
|
for (var exported in modeMethods) { exports[exported] = modeMethods[exported]; }
|
||||||
|
|
||||||
|
// Shim library CodeMirror with the minimal CodeMirror defined above.
|
||||||
|
require.cache[require.resolve("../../lib/codemirror")] = require.cache[require.resolve("./runmode.node")];
|
||||||
|
require.cache[require.resolve("../../addon/runmode/runmode")] = require.cache[require.resolve("./runmode.node")];
|
||||||
|
|
||||||
|
// Minimal default mode.
|
||||||
|
exports.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
|
||||||
|
exports.defineMIME("text/plain", "null");
|
||||||
|
|
||||||
|
exports.registerHelper = exports.registerGlobalHelper = Math.min;
|
||||||
|
exports.splitLines = function(string) { return string.split(/\r?\n|\r/) };
|
||||||
|
|
||||||
|
exports.defaults = { indentUnit: 2 };
|
||||||
|
|
||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
{ mod(require("../../lib/codemirror")); }
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
{ define(["../../lib/codemirror"], mod); }
|
||||||
|
else // Plain browser env
|
||||||
|
{ mod(CodeMirror); }
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
|
||||||
|
CodeMirror.runMode = function(string, modespec, callback, options) {
|
||||||
|
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
|
||||||
|
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
|
||||||
|
|
||||||
|
// Create a tokenizing callback function if passed-in callback is a DOM element.
|
||||||
|
if (callback.appendChild) {
|
||||||
|
var ie = /MSIE \d/.test(navigator.userAgent);
|
||||||
|
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
||||||
|
var node = callback, col = 0;
|
||||||
|
node.innerHTML = "";
|
||||||
|
callback = function(text, style) {
|
||||||
|
if (text == "\n") {
|
||||||
|
// Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
|
||||||
|
// Emitting a carriage return makes everything ok.
|
||||||
|
node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
|
||||||
|
col = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var content = "";
|
||||||
|
// replace tabs
|
||||||
|
for (var pos = 0;;) {
|
||||||
|
var idx = text.indexOf("\t", pos);
|
||||||
|
if (idx == -1) {
|
||||||
|
content += text.slice(pos);
|
||||||
|
col += text.length - pos;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
col += idx - pos;
|
||||||
|
content += text.slice(pos, idx);
|
||||||
|
var size = tabSize - col % tabSize;
|
||||||
|
col += size;
|
||||||
|
for (var i = 0; i < size; ++i) { content += " "; }
|
||||||
|
pos = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a node with token style and append it to the callback DOM element.
|
||||||
|
if (style) {
|
||||||
|
var sp = node.appendChild(document.createElement("span"));
|
||||||
|
sp.className = "cm-" + style.replace(/ +/g, " cm-");
|
||||||
|
sp.appendChild(document.createTextNode(content));
|
||||||
|
} else {
|
||||||
|
node.appendChild(document.createTextNode(content));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
|
||||||
|
for (var i = 0, e = lines.length; i < e; ++i) {
|
||||||
|
if (i) { callback("\n"); }
|
||||||
|
var stream = new CodeMirror.StringStream(lines[i], null, {
|
||||||
|
lookAhead: function(n) { return lines[i + n] },
|
||||||
|
baseToken: function() {}
|
||||||
|
});
|
||||||
|
if (!stream.string && mode.blankLine) { mode.blankLine(state); }
|
||||||
|
while (!stream.eol()) {
|
||||||
|
var style = mode.token(stream, state);
|
||||||
|
callback(stream.current(), style, i, stream.start, state);
|
||||||
|
stream.start = stream.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
128
src/modules/_CodeMirror/addon/scroll/annotatescrollbar.js
Normal file
128
src/modules/_CodeMirror/addon/scroll/annotatescrollbar.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("annotateScrollbar", function(options) {
|
||||||
|
if (typeof options == "string") options = {className: options};
|
||||||
|
return new Annotation(this, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineOption("scrollButtonHeight", 0);
|
||||||
|
|
||||||
|
function Annotation(cm, options) {
|
||||||
|
this.cm = cm;
|
||||||
|
this.options = options;
|
||||||
|
this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
|
||||||
|
this.annotations = [];
|
||||||
|
this.doRedraw = this.doUpdate = null;
|
||||||
|
this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
|
||||||
|
this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
|
||||||
|
this.computeScale();
|
||||||
|
|
||||||
|
function scheduleRedraw(delay) {
|
||||||
|
clearTimeout(self.doRedraw);
|
||||||
|
self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
cm.on("refresh", this.resizeHandler = function() {
|
||||||
|
clearTimeout(self.doUpdate);
|
||||||
|
self.doUpdate = setTimeout(function() {
|
||||||
|
if (self.computeScale()) scheduleRedraw(20);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
cm.on("markerAdded", this.resizeHandler);
|
||||||
|
cm.on("markerCleared", this.resizeHandler);
|
||||||
|
if (options.listenForChanges !== false)
|
||||||
|
cm.on("changes", this.changeHandler = function() {
|
||||||
|
scheduleRedraw(250);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotation.prototype.computeScale = function() {
|
||||||
|
var cm = this.cm;
|
||||||
|
var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
|
||||||
|
cm.getScrollerElement().scrollHeight
|
||||||
|
if (hScale != this.hScale) {
|
||||||
|
this.hScale = hScale;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Annotation.prototype.update = function(annotations) {
|
||||||
|
this.annotations = annotations;
|
||||||
|
this.redraw();
|
||||||
|
};
|
||||||
|
|
||||||
|
Annotation.prototype.redraw = function(compute) {
|
||||||
|
if (compute !== false) this.computeScale();
|
||||||
|
var cm = this.cm, hScale = this.hScale;
|
||||||
|
|
||||||
|
var frag = document.createDocumentFragment(), anns = this.annotations;
|
||||||
|
|
||||||
|
var wrapping = cm.getOption("lineWrapping");
|
||||||
|
var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
|
||||||
|
var curLine = null, curLineObj = null;
|
||||||
|
|
||||||
|
function getY(pos, top) {
|
||||||
|
if (curLine != pos.line) {
|
||||||
|
curLine = pos.line
|
||||||
|
curLineObj = cm.getLineHandle(pos.line)
|
||||||
|
var visual = cm.getLineHandleVisualStart(curLineObj)
|
||||||
|
if (visual != curLineObj) {
|
||||||
|
curLine = cm.getLineNumber(visual)
|
||||||
|
curLineObj = visual
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((curLineObj.widgets && curLineObj.widgets.length) ||
|
||||||
|
(wrapping && curLineObj.height > singleLineH))
|
||||||
|
return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
|
||||||
|
var topY = cm.heightAtLine(curLineObj, "local");
|
||||||
|
return topY + (top ? 0 : curLineObj.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastLine = cm.lastLine()
|
||||||
|
if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
|
||||||
|
var ann = anns[i];
|
||||||
|
if (ann.to.line > lastLine) continue;
|
||||||
|
var top = nextTop || getY(ann.from, true) * hScale;
|
||||||
|
var bottom = getY(ann.to, false) * hScale;
|
||||||
|
while (i < anns.length - 1) {
|
||||||
|
if (anns[i + 1].to.line > lastLine) break;
|
||||||
|
nextTop = getY(anns[i + 1].from, true) * hScale;
|
||||||
|
if (nextTop > bottom + .9) break;
|
||||||
|
ann = anns[++i];
|
||||||
|
bottom = getY(ann.to, false) * hScale;
|
||||||
|
}
|
||||||
|
if (bottom == top) continue;
|
||||||
|
var height = Math.max(bottom - top, 3);
|
||||||
|
|
||||||
|
var elt = frag.appendChild(document.createElement("div"));
|
||||||
|
elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
|
||||||
|
+ (top + this.buttonHeight) + "px; height: " + height + "px";
|
||||||
|
elt.className = this.options.className;
|
||||||
|
if (ann.id) {
|
||||||
|
elt.setAttribute("annotation-id", ann.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.div.textContent = "";
|
||||||
|
this.div.appendChild(frag);
|
||||||
|
};
|
||||||
|
|
||||||
|
Annotation.prototype.clear = function() {
|
||||||
|
this.cm.off("refresh", this.resizeHandler);
|
||||||
|
this.cm.off("markerAdded", this.resizeHandler);
|
||||||
|
this.cm.off("markerCleared", this.resizeHandler);
|
||||||
|
if (this.changeHandler) this.cm.off("changes", this.changeHandler);
|
||||||
|
this.div.parentNode.removeChild(this.div);
|
||||||
|
};
|
||||||
|
});
|
||||||
48
src/modules/_CodeMirror/addon/scroll/scrollpastend.js
Normal file
48
src/modules/_CodeMirror/addon/scroll/scrollpastend.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
cm.off("change", onChange);
|
||||||
|
cm.off("refresh", updateBottomMargin);
|
||||||
|
cm.display.lineSpace.parentNode.style.paddingBottom = "";
|
||||||
|
cm.state.scrollPastEndPadding = null;
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.on("change", onChange);
|
||||||
|
cm.on("refresh", updateBottomMargin);
|
||||||
|
updateBottomMargin(cm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onChange(cm, change) {
|
||||||
|
if (CodeMirror.changeEnd(change).line == cm.lastLine())
|
||||||
|
updateBottomMargin(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBottomMargin(cm) {
|
||||||
|
var padding = "";
|
||||||
|
if (cm.lineCount() > 1) {
|
||||||
|
var totalH = cm.display.scroller.clientHeight - 30,
|
||||||
|
lastLineH = cm.getLineHandle(cm.lastLine()).height;
|
||||||
|
padding = (totalH - lastLineH) + "px";
|
||||||
|
}
|
||||||
|
if (cm.state.scrollPastEndPadding != padding) {
|
||||||
|
cm.state.scrollPastEndPadding = padding;
|
||||||
|
cm.display.lineSpace.parentNode.style.paddingBottom = padding;
|
||||||
|
cm.off("refresh", updateBottomMargin);
|
||||||
|
cm.setSize();
|
||||||
|
cm.on("refresh", updateBottomMargin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
66
src/modules/_CodeMirror/addon/scroll/simplescrollbars.css
Normal file
66
src/modules/_CodeMirror/addon/scroll/simplescrollbars.css
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
|
||||||
|
position: absolute;
|
||||||
|
background: #ccc;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-simplescroll-horizontal {
|
||||||
|
bottom: 0; left: 0;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
.CodeMirror-simplescroll-horizontal div {
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-simplescroll-vertical {
|
||||||
|
right: 0; top: 0;
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
.CodeMirror-simplescroll-vertical div {
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
|
||||||
|
position: absolute;
|
||||||
|
background: #bcd;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-overlayscroll-horizontal {
|
||||||
|
bottom: 0; left: 0;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
.CodeMirror-overlayscroll-horizontal div {
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-overlayscroll-vertical {
|
||||||
|
right: 0; top: 0;
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
.CodeMirror-overlayscroll-vertical div {
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
152
src/modules/_CodeMirror/addon/scroll/simplescrollbars.js
Normal file
152
src/modules/_CodeMirror/addon/scroll/simplescrollbars.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function Bar(cls, orientation, scroll) {
|
||||||
|
this.orientation = orientation;
|
||||||
|
this.scroll = scroll;
|
||||||
|
this.screen = this.total = this.size = 1;
|
||||||
|
this.pos = 0;
|
||||||
|
|
||||||
|
this.node = document.createElement("div");
|
||||||
|
this.node.className = cls + "-" + orientation;
|
||||||
|
this.inner = this.node.appendChild(document.createElement("div"));
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
CodeMirror.on(this.inner, "mousedown", function(e) {
|
||||||
|
if (e.which != 1) return;
|
||||||
|
CodeMirror.e_preventDefault(e);
|
||||||
|
var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
|
||||||
|
var start = e[axis], startpos = self.pos;
|
||||||
|
function done() {
|
||||||
|
CodeMirror.off(document, "mousemove", move);
|
||||||
|
CodeMirror.off(document, "mouseup", done);
|
||||||
|
}
|
||||||
|
function move(e) {
|
||||||
|
if (e.which != 1) return done();
|
||||||
|
self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
|
||||||
|
}
|
||||||
|
CodeMirror.on(document, "mousemove", move);
|
||||||
|
CodeMirror.on(document, "mouseup", done);
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.on(this.node, "click", function(e) {
|
||||||
|
CodeMirror.e_preventDefault(e);
|
||||||
|
var innerBox = self.inner.getBoundingClientRect(), where;
|
||||||
|
if (self.orientation == "horizontal")
|
||||||
|
where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
|
||||||
|
else
|
||||||
|
where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
|
||||||
|
self.moveTo(self.pos + where * self.screen);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onWheel(e) {
|
||||||
|
var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
|
||||||
|
var oldPos = self.pos;
|
||||||
|
self.moveTo(self.pos + moved);
|
||||||
|
if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
|
||||||
|
}
|
||||||
|
CodeMirror.on(this.node, "mousewheel", onWheel);
|
||||||
|
CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bar.prototype.setPos = function(pos, force) {
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
if (pos > this.total - this.screen) pos = this.total - this.screen;
|
||||||
|
if (!force && pos == this.pos) return false;
|
||||||
|
this.pos = pos;
|
||||||
|
this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
|
||||||
|
(pos * (this.size / this.total)) + "px";
|
||||||
|
return true
|
||||||
|
};
|
||||||
|
|
||||||
|
Bar.prototype.moveTo = function(pos) {
|
||||||
|
if (this.setPos(pos)) this.scroll(pos, this.orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var minButtonSize = 10;
|
||||||
|
|
||||||
|
Bar.prototype.update = function(scrollSize, clientSize, barSize) {
|
||||||
|
var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
|
||||||
|
if (sizeChanged) {
|
||||||
|
this.screen = clientSize;
|
||||||
|
this.total = scrollSize;
|
||||||
|
this.size = barSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonSize = this.screen * (this.size / this.total);
|
||||||
|
if (buttonSize < minButtonSize) {
|
||||||
|
this.size -= minButtonSize - buttonSize;
|
||||||
|
buttonSize = minButtonSize;
|
||||||
|
}
|
||||||
|
this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
|
||||||
|
buttonSize + "px";
|
||||||
|
this.setPos(this.pos, sizeChanged);
|
||||||
|
};
|
||||||
|
|
||||||
|
function SimpleScrollbars(cls, place, scroll) {
|
||||||
|
this.addClass = cls;
|
||||||
|
this.horiz = new Bar(cls, "horizontal", scroll);
|
||||||
|
place(this.horiz.node);
|
||||||
|
this.vert = new Bar(cls, "vertical", scroll);
|
||||||
|
place(this.vert.node);
|
||||||
|
this.width = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleScrollbars.prototype.update = function(measure) {
|
||||||
|
if (this.width == null) {
|
||||||
|
var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
|
||||||
|
if (style) this.width = parseInt(style.height);
|
||||||
|
}
|
||||||
|
var width = this.width || 0;
|
||||||
|
|
||||||
|
var needsH = measure.scrollWidth > measure.clientWidth + 1;
|
||||||
|
var needsV = measure.scrollHeight > measure.clientHeight + 1;
|
||||||
|
this.vert.node.style.display = needsV ? "block" : "none";
|
||||||
|
this.horiz.node.style.display = needsH ? "block" : "none";
|
||||||
|
|
||||||
|
if (needsV) {
|
||||||
|
this.vert.update(measure.scrollHeight, measure.clientHeight,
|
||||||
|
measure.viewHeight - (needsH ? width : 0));
|
||||||
|
this.vert.node.style.bottom = needsH ? width + "px" : "0";
|
||||||
|
}
|
||||||
|
if (needsH) {
|
||||||
|
this.horiz.update(measure.scrollWidth, measure.clientWidth,
|
||||||
|
measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
|
||||||
|
this.horiz.node.style.right = needsV ? width + "px" : "0";
|
||||||
|
this.horiz.node.style.left = measure.barLeft + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {right: needsV ? width : 0, bottom: needsH ? width : 0};
|
||||||
|
};
|
||||||
|
|
||||||
|
SimpleScrollbars.prototype.setScrollTop = function(pos) {
|
||||||
|
this.vert.setPos(pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
SimpleScrollbars.prototype.setScrollLeft = function(pos) {
|
||||||
|
this.horiz.setPos(pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
SimpleScrollbars.prototype.clear = function() {
|
||||||
|
var parent = this.horiz.node.parentNode;
|
||||||
|
parent.removeChild(this.horiz.node);
|
||||||
|
parent.removeChild(this.vert.node);
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.scrollbarModel.simple = function(place, scroll) {
|
||||||
|
return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
|
||||||
|
};
|
||||||
|
CodeMirror.scrollbarModel.overlay = function(place, scroll) {
|
||||||
|
return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
|
||||||
|
};
|
||||||
|
});
|
||||||
50
src/modules/_CodeMirror/addon/search/jump-to-line.js
Normal file
50
src/modules/_CodeMirror/addon/search/jump-to-line.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Defines jumpToLine command. Uses dialog.js if present.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("../dialog/dialog"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "../dialog/dialog"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function dialog(cm, text, shortText, deflt, f) {
|
||||||
|
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
|
||||||
|
else f(prompt(shortText, deflt));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJumpDialog(cm) {
|
||||||
|
return cm.phrase("Jump to line:") + ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use line:column or scroll% syntax)") + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function interpretLine(cm, string) {
|
||||||
|
var num = Number(string)
|
||||||
|
if (/^[-+]/.test(string)) return cm.getCursor().line + num
|
||||||
|
else return num - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.commands.jumpToLine = function(cm) {
|
||||||
|
var cur = cm.getCursor();
|
||||||
|
dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) {
|
||||||
|
if (!posStr) return;
|
||||||
|
|
||||||
|
var match;
|
||||||
|
if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
|
||||||
|
cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
|
||||||
|
} else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
|
||||||
|
var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
|
||||||
|
if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
|
||||||
|
cm.setCursor(line - 1, cur.ch);
|
||||||
|
} else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
|
||||||
|
cm.setCursor(interpretLine(cm, match[1]), cur.ch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
|
||||||
|
});
|
||||||
167
src/modules/_CodeMirror/addon/search/match-highlighter.js
Normal file
167
src/modules/_CodeMirror/addon/search/match-highlighter.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Highlighting text that matches the selection
|
||||||
|
//
|
||||||
|
// Defines an option highlightSelectionMatches, which, when enabled,
|
||||||
|
// will style strings that match the selection throughout the
|
||||||
|
// document.
|
||||||
|
//
|
||||||
|
// The option can be set to true to simply enable it, or to a
|
||||||
|
// {minChars, style, wordsOnly, showToken, delay} object to explicitly
|
||||||
|
// configure it. minChars is the minimum amount of characters that should be
|
||||||
|
// selected for the behavior to occur, and style is the token style to
|
||||||
|
// apply to the matches. This will be prefixed by "cm-" to create an
|
||||||
|
// actual CSS class name. If wordsOnly is enabled, the matches will be
|
||||||
|
// highlighted only if the selected text is a word. showToken, when enabled,
|
||||||
|
// will cause the current token to be highlighted when nothing is selected.
|
||||||
|
// delay is used to specify how much time to wait, in milliseconds, before
|
||||||
|
// highlighting the matches. If annotateScrollbar is enabled, the occurences
|
||||||
|
// will be highlighted on the scrollbar via the matchesonscrollbar addon.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
style: "matchhighlight",
|
||||||
|
minChars: 2,
|
||||||
|
delay: 100,
|
||||||
|
wordsOnly: false,
|
||||||
|
annotateScrollbar: false,
|
||||||
|
showToken: false,
|
||||||
|
trim: true
|
||||||
|
}
|
||||||
|
|
||||||
|
function State(options) {
|
||||||
|
this.options = {}
|
||||||
|
for (var name in defaults)
|
||||||
|
this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
|
||||||
|
this.overlay = this.timeout = null;
|
||||||
|
this.matchesonscroll = null;
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init) {
|
||||||
|
removeOverlay(cm);
|
||||||
|
clearTimeout(cm.state.matchHighlighter.timeout);
|
||||||
|
cm.state.matchHighlighter = null;
|
||||||
|
cm.off("cursorActivity", cursorActivity);
|
||||||
|
cm.off("focus", onFocus)
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
var state = cm.state.matchHighlighter = new State(val);
|
||||||
|
if (cm.hasFocus()) {
|
||||||
|
state.active = true
|
||||||
|
highlightMatches(cm)
|
||||||
|
} else {
|
||||||
|
cm.on("focus", onFocus)
|
||||||
|
}
|
||||||
|
cm.on("cursorActivity", cursorActivity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function cursorActivity(cm) {
|
||||||
|
var state = cm.state.matchHighlighter;
|
||||||
|
if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFocus(cm) {
|
||||||
|
var state = cm.state.matchHighlighter
|
||||||
|
if (!state.active) {
|
||||||
|
state.active = true
|
||||||
|
scheduleHighlight(cm, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleHighlight(cm, state) {
|
||||||
|
clearTimeout(state.timeout);
|
||||||
|
state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOverlay(cm, query, hasBoundary, style) {
|
||||||
|
var state = cm.state.matchHighlighter;
|
||||||
|
cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
|
||||||
|
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
|
||||||
|
var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") +
|
||||||
|
query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") +
|
||||||
|
(/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query;
|
||||||
|
state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
|
||||||
|
{className: "CodeMirror-selection-highlight-scrollbar"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeOverlay(cm) {
|
||||||
|
var state = cm.state.matchHighlighter;
|
||||||
|
if (state.overlay) {
|
||||||
|
cm.removeOverlay(state.overlay);
|
||||||
|
state.overlay = null;
|
||||||
|
if (state.matchesonscroll) {
|
||||||
|
state.matchesonscroll.clear();
|
||||||
|
state.matchesonscroll = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightMatches(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
var state = cm.state.matchHighlighter;
|
||||||
|
removeOverlay(cm);
|
||||||
|
if (!cm.somethingSelected() && state.options.showToken) {
|
||||||
|
var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
|
||||||
|
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
||||||
|
while (start && re.test(line.charAt(start - 1))) --start;
|
||||||
|
while (end < line.length && re.test(line.charAt(end))) ++end;
|
||||||
|
if (start < end)
|
||||||
|
addOverlay(cm, line.slice(start, end), re, state.options.style);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||||
|
if (from.line != to.line) return;
|
||||||
|
if (state.options.wordsOnly && !isWord(cm, from, to)) return;
|
||||||
|
var selection = cm.getRange(from, to)
|
||||||
|
if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
|
||||||
|
if (selection.length >= state.options.minChars)
|
||||||
|
addOverlay(cm, selection, false, state.options.style);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWord(cm, from, to) {
|
||||||
|
var str = cm.getRange(from, to);
|
||||||
|
if (str.match(/^\w+$/) !== null) {
|
||||||
|
if (from.ch > 0) {
|
||||||
|
var pos = {line: from.line, ch: from.ch - 1};
|
||||||
|
var chr = cm.getRange(pos, from);
|
||||||
|
if (chr.match(/\W/) === null) return false;
|
||||||
|
}
|
||||||
|
if (to.ch < cm.getLine(from.line).length) {
|
||||||
|
var pos = {line: to.line, ch: to.ch + 1};
|
||||||
|
var chr = cm.getRange(to, pos);
|
||||||
|
if (chr.match(/\W/) === null) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function boundariesAround(stream, re) {
|
||||||
|
return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
|
||||||
|
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeOverlay(query, hasBoundary, style) {
|
||||||
|
return {token: function(stream) {
|
||||||
|
if (stream.match(query) &&
|
||||||
|
(!hasBoundary || boundariesAround(stream, hasBoundary)))
|
||||||
|
return style;
|
||||||
|
stream.next();
|
||||||
|
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.CodeMirror-search-match {
|
||||||
|
background: gold;
|
||||||
|
border-top: 1px solid orange;
|
||||||
|
border-bottom: 1px solid orange;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
97
src/modules/_CodeMirror/addon/search/matchesonscrollbar.js
Normal file
97
src/modules/_CodeMirror/addon/search/matchesonscrollbar.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
|
||||||
|
if (typeof options == "string") options = {className: options};
|
||||||
|
if (!options) options = {};
|
||||||
|
return new SearchAnnotation(this, query, caseFold, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
function SearchAnnotation(cm, query, caseFold, options) {
|
||||||
|
this.cm = cm;
|
||||||
|
this.options = options;
|
||||||
|
var annotateOptions = {listenForChanges: false};
|
||||||
|
for (var prop in options) annotateOptions[prop] = options[prop];
|
||||||
|
if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
|
||||||
|
this.annotation = cm.annotateScrollbar(annotateOptions);
|
||||||
|
this.query = query;
|
||||||
|
this.caseFold = caseFold;
|
||||||
|
this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
|
||||||
|
this.matches = [];
|
||||||
|
this.update = null;
|
||||||
|
|
||||||
|
this.findMatches();
|
||||||
|
this.annotation.update(this.matches);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
|
||||||
|
}
|
||||||
|
|
||||||
|
var MAX_MATCHES = 1000;
|
||||||
|
|
||||||
|
SearchAnnotation.prototype.findMatches = function() {
|
||||||
|
if (!this.gap) return;
|
||||||
|
for (var i = 0; i < this.matches.length; i++) {
|
||||||
|
var match = this.matches[i];
|
||||||
|
if (match.from.line >= this.gap.to) break;
|
||||||
|
if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
|
||||||
|
}
|
||||||
|
var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
|
||||||
|
var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
|
||||||
|
while (cursor.findNext()) {
|
||||||
|
var match = {from: cursor.from(), to: cursor.to()};
|
||||||
|
if (match.from.line >= this.gap.to) break;
|
||||||
|
this.matches.splice(i++, 0, match);
|
||||||
|
if (this.matches.length > maxMatches) break;
|
||||||
|
}
|
||||||
|
this.gap = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function offsetLine(line, changeStart, sizeChange) {
|
||||||
|
if (line <= changeStart) return line;
|
||||||
|
return Math.max(changeStart, line + sizeChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchAnnotation.prototype.onChange = function(change) {
|
||||||
|
var startLine = change.from.line;
|
||||||
|
var endLine = CodeMirror.changeEnd(change).line;
|
||||||
|
var sizeChange = endLine - change.to.line;
|
||||||
|
if (this.gap) {
|
||||||
|
this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
|
||||||
|
this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
|
||||||
|
} else {
|
||||||
|
this.gap = {from: change.from.line, to: endLine + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
|
||||||
|
var match = this.matches[i];
|
||||||
|
var newFrom = offsetLine(match.from.line, startLine, sizeChange);
|
||||||
|
if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
|
||||||
|
var newTo = offsetLine(match.to.line, startLine, sizeChange);
|
||||||
|
if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
|
||||||
|
}
|
||||||
|
clearTimeout(this.update);
|
||||||
|
var self = this;
|
||||||
|
this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchAnnotation.prototype.updateAfterChange = function() {
|
||||||
|
this.findMatches();
|
||||||
|
this.annotation.update(this.matches);
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchAnnotation.prototype.clear = function() {
|
||||||
|
this.cm.off("change", this.changeHandler);
|
||||||
|
this.annotation.clear();
|
||||||
|
};
|
||||||
|
});
|
||||||
260
src/modules/_CodeMirror/addon/search/search.js
Normal file
260
src/modules/_CodeMirror/addon/search/search.js
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Define search commands. Depends on dialog.js or another
|
||||||
|
// implementation of the openDialog method.
|
||||||
|
|
||||||
|
// Replace works a little oddly -- it will do the replace on the next
|
||||||
|
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
||||||
|
// replace by making sure the match is no longer selected when hitting
|
||||||
|
// Ctrl-G.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function searchOverlay(query, caseInsensitive) {
|
||||||
|
if (typeof query == "string")
|
||||||
|
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
|
||||||
|
else if (!query.global)
|
||||||
|
query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
|
||||||
|
|
||||||
|
return {token: function(stream) {
|
||||||
|
query.lastIndex = stream.pos;
|
||||||
|
var match = query.exec(stream.string);
|
||||||
|
if (match && match.index == stream.pos) {
|
||||||
|
stream.pos += match[0].length || 1;
|
||||||
|
return "searching";
|
||||||
|
} else if (match) {
|
||||||
|
stream.pos = match.index;
|
||||||
|
} else {
|
||||||
|
stream.skipToEnd();
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchState() {
|
||||||
|
this.posFrom = this.posTo = this.lastQuery = this.query = null;
|
||||||
|
this.overlay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchState(cm) {
|
||||||
|
return cm.state.search || (cm.state.search = new SearchState());
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryCaseInsensitive(query) {
|
||||||
|
return typeof query == "string" && query == query.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchCursor(cm, query, pos) {
|
||||||
|
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
||||||
|
return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
|
||||||
|
cm.openDialog(text, onEnter, {
|
||||||
|
value: deflt,
|
||||||
|
selectValueOnOpen: true,
|
||||||
|
closeOnEnter: false,
|
||||||
|
onClose: function() { clearSearch(cm); },
|
||||||
|
onKeyDown: onKeyDown
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dialog(cm, text, shortText, deflt, f) {
|
||||||
|
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
|
||||||
|
else f(prompt(shortText, deflt));
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmDialog(cm, text, shortText, fs) {
|
||||||
|
if (cm.openConfirm) cm.openConfirm(text, fs);
|
||||||
|
else if (confirm(shortText)) fs[0]();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseString(string) {
|
||||||
|
return string.replace(/\\([nrt\\])/g, function(match, ch) {
|
||||||
|
if (ch == "n") return "\n"
|
||||||
|
if (ch == "r") return "\r"
|
||||||
|
if (ch == "t") return "\t"
|
||||||
|
if (ch == "\\") return "\\"
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseQuery(query) {
|
||||||
|
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
||||||
|
if (isRE) {
|
||||||
|
try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
|
||||||
|
catch(e) {} // Not a regular expression after all, do a string search
|
||||||
|
} else {
|
||||||
|
query = parseString(query)
|
||||||
|
}
|
||||||
|
if (typeof query == "string" ? query == "" : query.test(""))
|
||||||
|
query = /x^/;
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSearch(cm, state, query) {
|
||||||
|
state.queryText = query;
|
||||||
|
state.query = parseQuery(query);
|
||||||
|
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
|
||||||
|
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
|
||||||
|
cm.addOverlay(state.overlay);
|
||||||
|
if (cm.showMatchesOnScrollbar) {
|
||||||
|
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||||
|
state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSearch(cm, rev, persistent, immediate) {
|
||||||
|
var state = getSearchState(cm);
|
||||||
|
if (state.query) return findNext(cm, rev);
|
||||||
|
var q = cm.getSelection() || state.lastQuery;
|
||||||
|
if (q instanceof RegExp && q.source == "x^") q = null
|
||||||
|
if (persistent && cm.openDialog) {
|
||||||
|
var hiding = null
|
||||||
|
var searchNext = function(query, event) {
|
||||||
|
CodeMirror.e_stop(event);
|
||||||
|
if (!query) return;
|
||||||
|
if (query != state.queryText) {
|
||||||
|
startSearch(cm, state, query);
|
||||||
|
state.posFrom = state.posTo = cm.getCursor();
|
||||||
|
}
|
||||||
|
if (hiding) hiding.style.opacity = 1
|
||||||
|
findNext(cm, event.shiftKey, function(_, to) {
|
||||||
|
var dialog
|
||||||
|
if (to.line < 3 && document.querySelector &&
|
||||||
|
(dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
|
||||||
|
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
|
||||||
|
(hiding = dialog).style.opacity = .4
|
||||||
|
})
|
||||||
|
};
|
||||||
|
persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
|
||||||
|
var keyName = CodeMirror.keyName(event)
|
||||||
|
var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
|
||||||
|
if (cmd == "findNext" || cmd == "findPrev" ||
|
||||||
|
cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
|
||||||
|
CodeMirror.e_stop(event);
|
||||||
|
startSearch(cm, getSearchState(cm), query);
|
||||||
|
cm.execCommand(cmd);
|
||||||
|
} else if (cmd == "find" || cmd == "findPersistent") {
|
||||||
|
CodeMirror.e_stop(event);
|
||||||
|
searchNext(query, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (immediate && q) {
|
||||||
|
startSearch(cm, state, q);
|
||||||
|
findNext(cm, rev);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
|
||||||
|
if (query && !state.query) cm.operation(function() {
|
||||||
|
startSearch(cm, state, query);
|
||||||
|
state.posFrom = state.posTo = cm.getCursor();
|
||||||
|
findNext(cm, rev);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findNext(cm, rev, callback) {cm.operation(function() {
|
||||||
|
var state = getSearchState(cm);
|
||||||
|
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
||||||
|
if (!cursor.find(rev)) {
|
||||||
|
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
||||||
|
if (!cursor.find(rev)) return;
|
||||||
|
}
|
||||||
|
cm.setSelection(cursor.from(), cursor.to());
|
||||||
|
cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
|
||||||
|
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
||||||
|
if (callback) callback(cursor.from(), cursor.to())
|
||||||
|
});}
|
||||||
|
|
||||||
|
function clearSearch(cm) {cm.operation(function() {
|
||||||
|
var state = getSearchState(cm);
|
||||||
|
state.lastQuery = state.query;
|
||||||
|
if (!state.query) return;
|
||||||
|
state.query = state.queryText = null;
|
||||||
|
cm.removeOverlay(state.overlay);
|
||||||
|
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||||
|
});}
|
||||||
|
|
||||||
|
|
||||||
|
function getQueryDialog(cm) {
|
||||||
|
return '<span class="CodeMirror-search-label">' + cm.phrase("Search:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
|
||||||
|
}
|
||||||
|
function getReplaceQueryDialog(cm) {
|
||||||
|
return ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
|
||||||
|
}
|
||||||
|
function getReplacementQueryDialog(cm) {
|
||||||
|
return '<span class="CodeMirror-search-label">' + cm.phrase("With:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
|
||||||
|
}
|
||||||
|
function getDoReplaceConfirm(cm) {
|
||||||
|
return '<span class="CodeMirror-search-label">' + cm.phrase("Replace?") + '</span> <button>' + cm.phrase("Yes") + '</button> <button>' + cm.phrase("No") + '</button> <button>' + cm.phrase("All") + '</button> <button>' + cm.phrase("Stop") + '</button> ';
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceAll(cm, query, text) {
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
||||||
|
if (typeof query != "string") {
|
||||||
|
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
||||||
|
cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
|
||||||
|
} else cursor.replace(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(cm, all) {
|
||||||
|
if (cm.getOption("readOnly")) return;
|
||||||
|
var query = cm.getSelection() || getSearchState(cm).lastQuery;
|
||||||
|
var dialogText = '<span class="CodeMirror-search-label">' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + '</span>';
|
||||||
|
dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) {
|
||||||
|
if (!query) return;
|
||||||
|
query = parseQuery(query);
|
||||||
|
dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
|
||||||
|
text = parseString(text)
|
||||||
|
if (all) {
|
||||||
|
replaceAll(cm, query, text)
|
||||||
|
} else {
|
||||||
|
clearSearch(cm);
|
||||||
|
var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
|
||||||
|
var advance = function() {
|
||||||
|
var start = cursor.from(), match;
|
||||||
|
if (!(match = cursor.findNext())) {
|
||||||
|
cursor = getSearchCursor(cm, query);
|
||||||
|
if (!(match = cursor.findNext()) ||
|
||||||
|
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
||||||
|
}
|
||||||
|
cm.setSelection(cursor.from(), cursor.to());
|
||||||
|
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
|
||||||
|
confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
|
||||||
|
[function() {doReplace(match);}, advance,
|
||||||
|
function() {replaceAll(cm, query, text)}]);
|
||||||
|
};
|
||||||
|
var doReplace = function(match) {
|
||||||
|
cursor.replace(typeof query == "string" ? text :
|
||||||
|
text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
|
||||||
|
advance();
|
||||||
|
};
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
||||||
|
CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
|
||||||
|
CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
|
||||||
|
CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
|
||||||
|
CodeMirror.commands.findNext = doSearch;
|
||||||
|
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
||||||
|
CodeMirror.commands.clearSearch = clearSearch;
|
||||||
|
CodeMirror.commands.replace = replace;
|
||||||
|
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
||||||
|
});
|
||||||
296
src/modules/_CodeMirror/addon/search/searchcursor.js
Normal file
296
src/modules/_CodeMirror/addon/search/searchcursor.js
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"))
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod)
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror)
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict"
|
||||||
|
var Pos = CodeMirror.Pos
|
||||||
|
|
||||||
|
function regexpFlags(regexp) {
|
||||||
|
var flags = regexp.flags
|
||||||
|
return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
|
||||||
|
+ (regexp.global ? "g" : "")
|
||||||
|
+ (regexp.multiline ? "m" : "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureFlags(regexp, flags) {
|
||||||
|
var current = regexpFlags(regexp), target = current
|
||||||
|
for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
|
||||||
|
target += flags.charAt(i)
|
||||||
|
return current == target ? regexp : new RegExp(regexp.source, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeMultiline(regexp) {
|
||||||
|
return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchRegexpForward(doc, regexp, start) {
|
||||||
|
regexp = ensureFlags(regexp, "g")
|
||||||
|
for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
|
||||||
|
regexp.lastIndex = ch
|
||||||
|
var string = doc.getLine(line), match = regexp.exec(string)
|
||||||
|
if (match)
|
||||||
|
return {from: Pos(line, match.index),
|
||||||
|
to: Pos(line, match.index + match[0].length),
|
||||||
|
match: match}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchRegexpForwardMultiline(doc, regexp, start) {
|
||||||
|
if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
|
||||||
|
|
||||||
|
regexp = ensureFlags(regexp, "gm")
|
||||||
|
var string, chunk = 1
|
||||||
|
for (var line = start.line, last = doc.lastLine(); line <= last;) {
|
||||||
|
// This grows the search buffer in exponentially-sized chunks
|
||||||
|
// between matches, so that nearby matches are fast and don't
|
||||||
|
// require concatenating the whole document (in case we're
|
||||||
|
// searching for something that has tons of matches), but at the
|
||||||
|
// same time, the amount of retries is limited.
|
||||||
|
for (var i = 0; i < chunk; i++) {
|
||||||
|
if (line > last) break
|
||||||
|
var curLine = doc.getLine(line++)
|
||||||
|
string = string == null ? curLine : string + "\n" + curLine
|
||||||
|
}
|
||||||
|
chunk = chunk * 2
|
||||||
|
regexp.lastIndex = start.ch
|
||||||
|
var match = regexp.exec(string)
|
||||||
|
if (match) {
|
||||||
|
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||||
|
var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
|
||||||
|
return {from: Pos(startLine, startCh),
|
||||||
|
to: Pos(startLine + inside.length - 1,
|
||||||
|
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||||
|
match: match}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lastMatchIn(string, regexp, endMargin) {
|
||||||
|
var match, from = 0
|
||||||
|
while (from <= string.length) {
|
||||||
|
regexp.lastIndex = from
|
||||||
|
var newMatch = regexp.exec(string)
|
||||||
|
if (!newMatch) break
|
||||||
|
var end = newMatch.index + newMatch[0].length
|
||||||
|
if (end > string.length - endMargin) break
|
||||||
|
if (!match || end > match.index + match[0].length)
|
||||||
|
match = newMatch
|
||||||
|
from = newMatch.index + 1
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchRegexpBackward(doc, regexp, start) {
|
||||||
|
regexp = ensureFlags(regexp, "g")
|
||||||
|
for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
|
||||||
|
var string = doc.getLine(line)
|
||||||
|
var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
|
||||||
|
if (match)
|
||||||
|
return {from: Pos(line, match.index),
|
||||||
|
to: Pos(line, match.index + match[0].length),
|
||||||
|
match: match}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchRegexpBackwardMultiline(doc, regexp, start) {
|
||||||
|
if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
|
||||||
|
regexp = ensureFlags(regexp, "gm")
|
||||||
|
var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
|
||||||
|
for (var line = start.line, first = doc.firstLine(); line >= first;) {
|
||||||
|
for (var i = 0; i < chunkSize && line >= first; i++) {
|
||||||
|
var curLine = doc.getLine(line--)
|
||||||
|
string = string == null ? curLine : curLine + "\n" + string
|
||||||
|
}
|
||||||
|
chunkSize *= 2
|
||||||
|
|
||||||
|
var match = lastMatchIn(string, regexp, endMargin)
|
||||||
|
if (match) {
|
||||||
|
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||||
|
var startLine = line + before.length, startCh = before[before.length - 1].length
|
||||||
|
return {from: Pos(startLine, startCh),
|
||||||
|
to: Pos(startLine + inside.length - 1,
|
||||||
|
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||||
|
match: match}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var doFold, noFold
|
||||||
|
if (String.prototype.normalize) {
|
||||||
|
doFold = function(str) { return str.normalize("NFD").toLowerCase() }
|
||||||
|
noFold = function(str) { return str.normalize("NFD") }
|
||||||
|
} else {
|
||||||
|
doFold = function(str) { return str.toLowerCase() }
|
||||||
|
noFold = function(str) { return str }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a position in a case-folded line back to a position in the original line
|
||||||
|
// (compensating for codepoints increasing in number during folding)
|
||||||
|
function adjustPos(orig, folded, pos, foldFunc) {
|
||||||
|
if (orig.length == folded.length) return pos
|
||||||
|
for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
|
||||||
|
if (min == max) return min
|
||||||
|
var mid = (min + max) >> 1
|
||||||
|
var len = foldFunc(orig.slice(0, mid)).length
|
||||||
|
if (len == pos) return mid
|
||||||
|
else if (len > pos) max = mid
|
||||||
|
else min = mid + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchStringForward(doc, query, start, caseFold) {
|
||||||
|
// Empty string would match anything and never progress, so we
|
||||||
|
// define it to match nothing instead.
|
||||||
|
if (!query.length) return null
|
||||||
|
var fold = caseFold ? doFold : noFold
|
||||||
|
var lines = fold(query).split(/\r|\n\r?/)
|
||||||
|
|
||||||
|
search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
|
||||||
|
var orig = doc.getLine(line).slice(ch), string = fold(orig)
|
||||||
|
if (lines.length == 1) {
|
||||||
|
var found = string.indexOf(lines[0])
|
||||||
|
if (found == -1) continue search
|
||||||
|
var start = adjustPos(orig, string, found, fold) + ch
|
||||||
|
return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
|
||||||
|
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
|
||||||
|
} else {
|
||||||
|
var cutFrom = string.length - lines[0].length
|
||||||
|
if (string.slice(cutFrom) != lines[0]) continue search
|
||||||
|
for (var i = 1; i < lines.length - 1; i++)
|
||||||
|
if (fold(doc.getLine(line + i)) != lines[i]) continue search
|
||||||
|
var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
|
||||||
|
if (endString.slice(0, lastLine.length) != lastLine) continue search
|
||||||
|
return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
|
||||||
|
to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchStringBackward(doc, query, start, caseFold) {
|
||||||
|
if (!query.length) return null
|
||||||
|
var fold = caseFold ? doFold : noFold
|
||||||
|
var lines = fold(query).split(/\r|\n\r?/)
|
||||||
|
|
||||||
|
search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
|
||||||
|
var orig = doc.getLine(line)
|
||||||
|
if (ch > -1) orig = orig.slice(0, ch)
|
||||||
|
var string = fold(orig)
|
||||||
|
if (lines.length == 1) {
|
||||||
|
var found = string.lastIndexOf(lines[0])
|
||||||
|
if (found == -1) continue search
|
||||||
|
return {from: Pos(line, adjustPos(orig, string, found, fold)),
|
||||||
|
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
|
||||||
|
} else {
|
||||||
|
var lastLine = lines[lines.length - 1]
|
||||||
|
if (string.slice(0, lastLine.length) != lastLine) continue search
|
||||||
|
for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
|
||||||
|
if (fold(doc.getLine(start + i)) != lines[i]) continue search
|
||||||
|
var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
|
||||||
|
if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
|
||||||
|
return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
|
||||||
|
to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchCursor(doc, query, pos, options) {
|
||||||
|
this.atOccurrence = false
|
||||||
|
this.doc = doc
|
||||||
|
pos = pos ? doc.clipPos(pos) : Pos(0, 0)
|
||||||
|
this.pos = {from: pos, to: pos}
|
||||||
|
|
||||||
|
var caseFold
|
||||||
|
if (typeof options == "object") {
|
||||||
|
caseFold = options.caseFold
|
||||||
|
} else { // Backwards compat for when caseFold was the 4th argument
|
||||||
|
caseFold = options
|
||||||
|
options = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof query == "string") {
|
||||||
|
if (caseFold == null) caseFold = false
|
||||||
|
this.matches = function(reverse, pos) {
|
||||||
|
return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query = ensureFlags(query, "gm")
|
||||||
|
if (!options || options.multiline !== false)
|
||||||
|
this.matches = function(reverse, pos) {
|
||||||
|
return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.matches = function(reverse, pos) {
|
||||||
|
return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchCursor.prototype = {
|
||||||
|
findNext: function() {return this.find(false)},
|
||||||
|
findPrevious: function() {return this.find(true)},
|
||||||
|
|
||||||
|
find: function(reverse) {
|
||||||
|
var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
|
||||||
|
|
||||||
|
// Implements weird auto-growing behavior on null-matches for
|
||||||
|
// backwards-compatibility with the vim code (unfortunately)
|
||||||
|
while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
|
||||||
|
if (reverse) {
|
||||||
|
if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
|
||||||
|
else if (result.from.line == this.doc.firstLine()) result = null
|
||||||
|
else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
|
||||||
|
} else {
|
||||||
|
if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
|
||||||
|
else if (result.to.line == this.doc.lastLine()) result = null
|
||||||
|
else result = this.matches(reverse, Pos(result.to.line + 1, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
this.pos = result
|
||||||
|
this.atOccurrence = true
|
||||||
|
return this.pos.match || true
|
||||||
|
} else {
|
||||||
|
var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
|
||||||
|
this.pos = {from: end, to: end}
|
||||||
|
return this.atOccurrence = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
from: function() {if (this.atOccurrence) return this.pos.from},
|
||||||
|
to: function() {if (this.atOccurrence) return this.pos.to},
|
||||||
|
|
||||||
|
replace: function(newText, origin) {
|
||||||
|
if (!this.atOccurrence) return
|
||||||
|
var lines = CodeMirror.splitLines(newText)
|
||||||
|
this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
|
||||||
|
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
|
||||||
|
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||||
|
return new SearchCursor(this.doc, query, pos, caseFold)
|
||||||
|
})
|
||||||
|
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||||
|
return new SearchCursor(this, query, pos, caseFold)
|
||||||
|
})
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
|
||||||
|
var ranges = []
|
||||||
|
var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
|
||||||
|
while (cur.findNext()) {
|
||||||
|
if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
|
||||||
|
ranges.push({anchor: cur.from(), head: cur.to()})
|
||||||
|
}
|
||||||
|
if (ranges.length)
|
||||||
|
this.setSelections(ranges, 0)
|
||||||
|
})
|
||||||
|
});
|
||||||
72
src/modules/_CodeMirror/addon/selection/active-line.js
Normal file
72
src/modules/_CodeMirror/addon/selection/active-line.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
var WRAP_CLASS = "CodeMirror-activeline";
|
||||||
|
var BACK_CLASS = "CodeMirror-activeline-background";
|
||||||
|
var GUTT_CLASS = "CodeMirror-activeline-gutter";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
|
||||||
|
var prev = old == CodeMirror.Init ? false : old;
|
||||||
|
if (val == prev) return
|
||||||
|
if (prev) {
|
||||||
|
cm.off("beforeSelectionChange", selectionChange);
|
||||||
|
clearActiveLines(cm);
|
||||||
|
delete cm.state.activeLines;
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.state.activeLines = [];
|
||||||
|
updateActiveLines(cm, cm.listSelections());
|
||||||
|
cm.on("beforeSelectionChange", selectionChange);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearActiveLines(cm) {
|
||||||
|
for (var i = 0; i < cm.state.activeLines.length; i++) {
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameArray(a, b) {
|
||||||
|
if (a.length != b.length) return false;
|
||||||
|
for (var i = 0; i < a.length; i++)
|
||||||
|
if (a[i] != b[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActiveLines(cm, ranges) {
|
||||||
|
var active = [];
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var range = ranges[i];
|
||||||
|
var option = cm.getOption("styleActiveLine");
|
||||||
|
if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
|
||||||
|
continue
|
||||||
|
var line = cm.getLineHandleVisualStart(range.head.line);
|
||||||
|
if (active[active.length - 1] != line) active.push(line);
|
||||||
|
}
|
||||||
|
if (sameArray(cm.state.activeLines, active)) return;
|
||||||
|
cm.operation(function() {
|
||||||
|
clearActiveLines(cm);
|
||||||
|
for (var i = 0; i < active.length; i++) {
|
||||||
|
cm.addLineClass(active[i], "wrap", WRAP_CLASS);
|
||||||
|
cm.addLineClass(active[i], "background", BACK_CLASS);
|
||||||
|
cm.addLineClass(active[i], "gutter", GUTT_CLASS);
|
||||||
|
}
|
||||||
|
cm.state.activeLines = active;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectionChange(cm, sel) {
|
||||||
|
updateActiveLines(cm, sel.ranges);
|
||||||
|
}
|
||||||
|
});
|
||||||
119
src/modules/_CodeMirror/addon/selection/mark-selection.js
Normal file
119
src/modules/_CodeMirror/addon/selection/mark-selection.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Because sometimes you need to mark the selected *text*.
|
||||||
|
//
|
||||||
|
// Adds an option 'styleSelectedText' which, when enabled, gives
|
||||||
|
// selected text the CSS class given as option value, or
|
||||||
|
// "CodeMirror-selectedtext" when the value is not a string.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
|
||||||
|
var prev = old && old != CodeMirror.Init;
|
||||||
|
if (val && !prev) {
|
||||||
|
cm.state.markedSelection = [];
|
||||||
|
cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
|
||||||
|
reset(cm);
|
||||||
|
cm.on("cursorActivity", onCursorActivity);
|
||||||
|
cm.on("change", onChange);
|
||||||
|
} else if (!val && prev) {
|
||||||
|
cm.off("cursorActivity", onCursorActivity);
|
||||||
|
cm.off("change", onChange);
|
||||||
|
clear(cm);
|
||||||
|
cm.state.markedSelection = cm.state.markedSelectionStyle = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onCursorActivity(cm) {
|
||||||
|
if (cm.state.markedSelection)
|
||||||
|
cm.operation(function() { update(cm); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(cm) {
|
||||||
|
if (cm.state.markedSelection && cm.state.markedSelection.length)
|
||||||
|
cm.operation(function() { clear(cm); });
|
||||||
|
}
|
||||||
|
|
||||||
|
var CHUNK_SIZE = 8;
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
var cmp = CodeMirror.cmpPos;
|
||||||
|
|
||||||
|
function coverRange(cm, from, to, addAt) {
|
||||||
|
if (cmp(from, to) == 0) return;
|
||||||
|
var array = cm.state.markedSelection;
|
||||||
|
var cls = cm.state.markedSelectionStyle;
|
||||||
|
for (var line = from.line;;) {
|
||||||
|
var start = line == from.line ? from : Pos(line, 0);
|
||||||
|
var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
|
||||||
|
var end = atEnd ? to : Pos(endLine, 0);
|
||||||
|
var mark = cm.markText(start, end, {className: cls});
|
||||||
|
if (addAt == null) array.push(mark);
|
||||||
|
else array.splice(addAt++, 0, mark);
|
||||||
|
if (atEnd) break;
|
||||||
|
line = endLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(cm) {
|
||||||
|
var array = cm.state.markedSelection;
|
||||||
|
for (var i = 0; i < array.length; ++i) array[i].clear();
|
||||||
|
array.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(cm) {
|
||||||
|
clear(cm);
|
||||||
|
var ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++)
|
||||||
|
coverRange(cm, ranges[i].from(), ranges[i].to());
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(cm) {
|
||||||
|
if (!cm.somethingSelected()) return clear(cm);
|
||||||
|
if (cm.listSelections().length > 1) return reset(cm);
|
||||||
|
|
||||||
|
var from = cm.getCursor("start"), to = cm.getCursor("end");
|
||||||
|
|
||||||
|
var array = cm.state.markedSelection;
|
||||||
|
if (!array.length) return coverRange(cm, from, to);
|
||||||
|
|
||||||
|
var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
|
||||||
|
if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
|
||||||
|
cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
|
||||||
|
return reset(cm);
|
||||||
|
|
||||||
|
while (cmp(from, coverStart.from) > 0) {
|
||||||
|
array.shift().clear();
|
||||||
|
coverStart = array[0].find();
|
||||||
|
}
|
||||||
|
if (cmp(from, coverStart.from) < 0) {
|
||||||
|
if (coverStart.to.line - from.line < CHUNK_SIZE) {
|
||||||
|
array.shift().clear();
|
||||||
|
coverRange(cm, from, coverStart.to, 0);
|
||||||
|
} else {
|
||||||
|
coverRange(cm, from, coverStart.from, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cmp(to, coverEnd.to) < 0) {
|
||||||
|
array.pop().clear();
|
||||||
|
coverEnd = array[array.length - 1].find();
|
||||||
|
}
|
||||||
|
if (cmp(to, coverEnd.to) > 0) {
|
||||||
|
if (to.line - coverEnd.from.line < CHUNK_SIZE) {
|
||||||
|
array.pop().clear();
|
||||||
|
coverRange(cm, coverEnd.from, to);
|
||||||
|
} else {
|
||||||
|
coverRange(cm, coverEnd.to, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
98
src/modules/_CodeMirror/addon/selection/selection-pointer.js
Normal file
98
src/modules/_CodeMirror/addon/selection/selection-pointer.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("selectionPointer", false, function(cm, val) {
|
||||||
|
var data = cm.state.selectionPointer;
|
||||||
|
if (data) {
|
||||||
|
CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
|
||||||
|
CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
|
||||||
|
CodeMirror.off(window, "scroll", data.windowScroll);
|
||||||
|
cm.off("cursorActivity", reset);
|
||||||
|
cm.off("scroll", reset);
|
||||||
|
cm.state.selectionPointer = null;
|
||||||
|
cm.display.lineDiv.style.cursor = "";
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
data = cm.state.selectionPointer = {
|
||||||
|
value: typeof val == "string" ? val : "default",
|
||||||
|
mousemove: function(event) { mousemove(cm, event); },
|
||||||
|
mouseout: function(event) { mouseout(cm, event); },
|
||||||
|
windowScroll: function() { reset(cm); },
|
||||||
|
rects: null,
|
||||||
|
mouseX: null, mouseY: null,
|
||||||
|
willUpdate: false
|
||||||
|
};
|
||||||
|
CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
|
||||||
|
CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
|
||||||
|
CodeMirror.on(window, "scroll", data.windowScroll);
|
||||||
|
cm.on("cursorActivity", reset);
|
||||||
|
cm.on("scroll", reset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function mousemove(cm, event) {
|
||||||
|
var data = cm.state.selectionPointer;
|
||||||
|
if (event.buttons == null ? event.which : event.buttons) {
|
||||||
|
data.mouseX = data.mouseY = null;
|
||||||
|
} else {
|
||||||
|
data.mouseX = event.clientX;
|
||||||
|
data.mouseY = event.clientY;
|
||||||
|
}
|
||||||
|
scheduleUpdate(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseout(cm, event) {
|
||||||
|
if (!cm.getWrapperElement().contains(event.relatedTarget)) {
|
||||||
|
var data = cm.state.selectionPointer;
|
||||||
|
data.mouseX = data.mouseY = null;
|
||||||
|
scheduleUpdate(cm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(cm) {
|
||||||
|
cm.state.selectionPointer.rects = null;
|
||||||
|
scheduleUpdate(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleUpdate(cm) {
|
||||||
|
if (!cm.state.selectionPointer.willUpdate) {
|
||||||
|
cm.state.selectionPointer.willUpdate = true;
|
||||||
|
setTimeout(function() {
|
||||||
|
update(cm);
|
||||||
|
cm.state.selectionPointer.willUpdate = false;
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(cm) {
|
||||||
|
var data = cm.state.selectionPointer;
|
||||||
|
if (!data) return;
|
||||||
|
if (data.rects == null && data.mouseX != null) {
|
||||||
|
data.rects = [];
|
||||||
|
if (cm.somethingSelected()) {
|
||||||
|
for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)
|
||||||
|
data.rects.push(sel.getBoundingClientRect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var inside = false;
|
||||||
|
if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {
|
||||||
|
var rect = data.rects[i];
|
||||||
|
if (rect.left <= data.mouseX && rect.right >= data.mouseX &&
|
||||||
|
rect.top <= data.mouseY && rect.bottom >= data.mouseY)
|
||||||
|
inside = true;
|
||||||
|
}
|
||||||
|
var cursor = inside ? data.value : "";
|
||||||
|
if (cm.display.lineDiv.style.cursor != cursor)
|
||||||
|
cm.display.lineDiv.style.cursor = cursor;
|
||||||
|
}
|
||||||
|
});
|
||||||
87
src/modules/_CodeMirror/addon/tern/tern.css
Normal file
87
src/modules/_CodeMirror/addon/tern/tern.css
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
.CodeMirror-Tern-completion {
|
||||||
|
padding-left: 22px;
|
||||||
|
position: relative;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion:before {
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 15px;
|
||||||
|
width: 15px;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-unknown:before {
|
||||||
|
content: "?";
|
||||||
|
background: #4bb;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-object:before {
|
||||||
|
content: "O";
|
||||||
|
background: #77c;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-fn:before {
|
||||||
|
content: "F";
|
||||||
|
background: #7c7;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-array:before {
|
||||||
|
content: "A";
|
||||||
|
background: #c66;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-number:before {
|
||||||
|
content: "1";
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-string:before {
|
||||||
|
content: "S";
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-completion-bool:before {
|
||||||
|
content: "B";
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-Tern-completion-guess {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-Tern-tooltip {
|
||||||
|
border: 1px solid silver;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #444;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 90%;
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: white;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
|
||||||
|
max-width: 40em;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
|
||||||
|
transition: opacity 1s;
|
||||||
|
-moz-transition: opacity 1s;
|
||||||
|
-webkit-transition: opacity 1s;
|
||||||
|
-o-transition: opacity 1s;
|
||||||
|
-ms-transition: opacity 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-Tern-hint-doc {
|
||||||
|
max-width: 25em;
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-Tern-fname { color: black; }
|
||||||
|
.CodeMirror-Tern-farg { color: #70a; }
|
||||||
|
.CodeMirror-Tern-farg-current { text-decoration: underline; }
|
||||||
|
.CodeMirror-Tern-type { color: #07c; }
|
||||||
|
.CodeMirror-Tern-fhint-guess { opacity: .7; }
|
||||||
719
src/modules/_CodeMirror/addon/tern/tern.js
Normal file
719
src/modules/_CodeMirror/addon/tern/tern.js
Normal file
@@ -0,0 +1,719 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// Glue code between CodeMirror and Tern.
|
||||||
|
//
|
||||||
|
// Create a CodeMirror.TernServer to wrap an actual Tern server,
|
||||||
|
// register open documents (CodeMirror.Doc instances) with it, and
|
||||||
|
// call its methods to activate the assisting functions that Tern
|
||||||
|
// provides.
|
||||||
|
//
|
||||||
|
// Options supported (all optional):
|
||||||
|
// * defs: An array of JSON definition data structures.
|
||||||
|
// * plugins: An object mapping plugin names to configuration
|
||||||
|
// options.
|
||||||
|
// * getFile: A function(name, c) that can be used to access files in
|
||||||
|
// the project that haven't been loaded yet. Simply do c(null) to
|
||||||
|
// indicate that a file is not available.
|
||||||
|
// * fileFilter: A function(value, docName, doc) that will be applied
|
||||||
|
// to documents before passing them on to Tern.
|
||||||
|
// * switchToDoc: A function(name, doc) that should, when providing a
|
||||||
|
// multi-file view, switch the view or focus to the named file.
|
||||||
|
// * showError: A function(editor, message) that can be used to
|
||||||
|
// override the way errors are displayed.
|
||||||
|
// * completionTip: Customize the content in tooltips for completions.
|
||||||
|
// Is passed a single argument—the completion's data as returned by
|
||||||
|
// Tern—and may return a string, DOM node, or null to indicate that
|
||||||
|
// no tip should be shown. By default the docstring is shown.
|
||||||
|
// * typeTip: Like completionTip, but for the tooltips shown for type
|
||||||
|
// queries.
|
||||||
|
// * responseFilter: A function(doc, query, request, error, data) that
|
||||||
|
// will be applied to the Tern responses before treating them
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// It is possible to run the Tern server in a web worker by specifying
|
||||||
|
// these additional options:
|
||||||
|
// * useWorker: Set to true to enable web worker mode. You'll probably
|
||||||
|
// want to feature detect the actual value you use here, for example
|
||||||
|
// !!window.Worker.
|
||||||
|
// * workerScript: The main script of the worker. Point this to
|
||||||
|
// wherever you are hosting worker.js from this directory.
|
||||||
|
// * workerDeps: An array of paths pointing (relative to workerScript)
|
||||||
|
// to the Acorn and Tern libraries and any Tern plugins you want to
|
||||||
|
// load. Or, if you minified those into a single script and included
|
||||||
|
// them in the workerScript, simply leave this undefined.
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
// declare global: tern
|
||||||
|
|
||||||
|
CodeMirror.TernServer = function(options) {
|
||||||
|
var self = this;
|
||||||
|
this.options = options || {};
|
||||||
|
var plugins = this.options.plugins || (this.options.plugins = {});
|
||||||
|
if (!plugins.doc_comment) plugins.doc_comment = true;
|
||||||
|
this.docs = Object.create(null);
|
||||||
|
if (this.options.useWorker) {
|
||||||
|
this.server = new WorkerServer(this);
|
||||||
|
} else {
|
||||||
|
this.server = new tern.Server({
|
||||||
|
getFile: function(name, c) { return getFile(self, name, c); },
|
||||||
|
async: true,
|
||||||
|
defs: this.options.defs || [],
|
||||||
|
plugins: plugins
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.trackChange = function(doc, change) { trackChange(self, doc, change); };
|
||||||
|
|
||||||
|
this.cachedArgHints = null;
|
||||||
|
this.activeArgHints = null;
|
||||||
|
this.jumpStack = [];
|
||||||
|
|
||||||
|
this.getHint = function(cm, c) { return hint(self, cm, c); };
|
||||||
|
this.getHint.async = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.TernServer.prototype = {
|
||||||
|
addDoc: function(name, doc) {
|
||||||
|
var data = {doc: doc, name: name, changed: null};
|
||||||
|
this.server.addFile(name, docValue(this, data));
|
||||||
|
CodeMirror.on(doc, "change", this.trackChange);
|
||||||
|
return this.docs[name] = data;
|
||||||
|
},
|
||||||
|
|
||||||
|
delDoc: function(id) {
|
||||||
|
var found = resolveDoc(this, id);
|
||||||
|
if (!found) return;
|
||||||
|
CodeMirror.off(found.doc, "change", this.trackChange);
|
||||||
|
delete this.docs[found.name];
|
||||||
|
this.server.delFile(found.name);
|
||||||
|
},
|
||||||
|
|
||||||
|
hideDoc: function(id) {
|
||||||
|
closeArgHints(this);
|
||||||
|
var found = resolveDoc(this, id);
|
||||||
|
if (found && found.changed) sendDoc(this, found);
|
||||||
|
},
|
||||||
|
|
||||||
|
complete: function(cm) {
|
||||||
|
cm.showHint({hint: this.getHint});
|
||||||
|
},
|
||||||
|
|
||||||
|
showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
|
||||||
|
|
||||||
|
showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
|
||||||
|
|
||||||
|
updateArgHints: function(cm) { updateArgHints(this, cm); },
|
||||||
|
|
||||||
|
jumpToDef: function(cm) { jumpToDef(this, cm); },
|
||||||
|
|
||||||
|
jumpBack: function(cm) { jumpBack(this, cm); },
|
||||||
|
|
||||||
|
rename: function(cm) { rename(this, cm); },
|
||||||
|
|
||||||
|
selectName: function(cm) { selectName(this, cm); },
|
||||||
|
|
||||||
|
request: function (cm, query, c, pos) {
|
||||||
|
var self = this;
|
||||||
|
var doc = findDoc(this, cm.getDoc());
|
||||||
|
var request = buildRequest(this, doc, query, pos);
|
||||||
|
var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]
|
||||||
|
if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];
|
||||||
|
|
||||||
|
this.server.request(request, function (error, data) {
|
||||||
|
if (!error && self.options.responseFilter)
|
||||||
|
data = self.options.responseFilter(doc, query, request, error, data);
|
||||||
|
c(error, data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function () {
|
||||||
|
closeArgHints(this)
|
||||||
|
if (this.worker) {
|
||||||
|
this.worker.terminate();
|
||||||
|
this.worker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
var cls = "CodeMirror-Tern-";
|
||||||
|
var bigDoc = 250;
|
||||||
|
|
||||||
|
function getFile(ts, name, c) {
|
||||||
|
var buf = ts.docs[name];
|
||||||
|
if (buf)
|
||||||
|
c(docValue(ts, buf));
|
||||||
|
else if (ts.options.getFile)
|
||||||
|
ts.options.getFile(name, c);
|
||||||
|
else
|
||||||
|
c(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findDoc(ts, doc, name) {
|
||||||
|
for (var n in ts.docs) {
|
||||||
|
var cur = ts.docs[n];
|
||||||
|
if (cur.doc == doc) return cur;
|
||||||
|
}
|
||||||
|
if (!name) for (var i = 0;; ++i) {
|
||||||
|
n = "[doc" + (i || "") + "]";
|
||||||
|
if (!ts.docs[n]) { name = n; break; }
|
||||||
|
}
|
||||||
|
return ts.addDoc(name, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDoc(ts, id) {
|
||||||
|
if (typeof id == "string") return ts.docs[id];
|
||||||
|
if (id instanceof CodeMirror) id = id.getDoc();
|
||||||
|
if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackChange(ts, doc, change) {
|
||||||
|
var data = findDoc(ts, doc);
|
||||||
|
|
||||||
|
var argHints = ts.cachedArgHints;
|
||||||
|
if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)
|
||||||
|
ts.cachedArgHints = null;
|
||||||
|
|
||||||
|
var changed = data.changed;
|
||||||
|
if (changed == null)
|
||||||
|
data.changed = changed = {from: change.from.line, to: change.from.line};
|
||||||
|
var end = change.from.line + (change.text.length - 1);
|
||||||
|
if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
|
||||||
|
if (end >= changed.to) changed.to = end + 1;
|
||||||
|
if (changed.from > change.from.line) changed.from = change.from.line;
|
||||||
|
|
||||||
|
if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
|
||||||
|
if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendDoc(ts, doc) {
|
||||||
|
ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
|
||||||
|
if (error) window.console.error(error);
|
||||||
|
else doc.changed = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completion
|
||||||
|
|
||||||
|
function hint(ts, cm, c) {
|
||||||
|
ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
|
||||||
|
if (error) return showError(ts, cm, error);
|
||||||
|
var completions = [], after = "";
|
||||||
|
var from = data.start, to = data.end;
|
||||||
|
if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
|
||||||
|
cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
|
||||||
|
after = "\"]";
|
||||||
|
|
||||||
|
for (var i = 0; i < data.completions.length; ++i) {
|
||||||
|
var completion = data.completions[i], className = typeToIcon(completion.type);
|
||||||
|
if (data.guess) className += " " + cls + "guess";
|
||||||
|
completions.push({text: completion.name + after,
|
||||||
|
displayText: completion.displayName || completion.name,
|
||||||
|
className: className,
|
||||||
|
data: completion});
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = {from: from, to: to, list: completions};
|
||||||
|
var tooltip = null;
|
||||||
|
CodeMirror.on(obj, "close", function() { remove(tooltip); });
|
||||||
|
CodeMirror.on(obj, "update", function() { remove(tooltip); });
|
||||||
|
CodeMirror.on(obj, "select", function(cur, node) {
|
||||||
|
remove(tooltip);
|
||||||
|
var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
|
||||||
|
if (content) {
|
||||||
|
tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
|
||||||
|
node.getBoundingClientRect().top + window.pageYOffset, content, cm);
|
||||||
|
tooltip.className += " " + cls + "hint-doc";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
c(obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeToIcon(type) {
|
||||||
|
var suffix;
|
||||||
|
if (type == "?") suffix = "unknown";
|
||||||
|
else if (type == "number" || type == "string" || type == "bool") suffix = type;
|
||||||
|
else if (/^fn\(/.test(type)) suffix = "fn";
|
||||||
|
else if (/^\[/.test(type)) suffix = "array";
|
||||||
|
else suffix = "object";
|
||||||
|
return cls + "completion " + cls + "completion-" + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type queries
|
||||||
|
|
||||||
|
function showContextInfo(ts, cm, pos, queryName, c) {
|
||||||
|
ts.request(cm, queryName, function(error, data) {
|
||||||
|
if (error) return showError(ts, cm, error);
|
||||||
|
if (ts.options.typeTip) {
|
||||||
|
var tip = ts.options.typeTip(data);
|
||||||
|
} else {
|
||||||
|
var tip = elt("span", null, elt("strong", null, data.type || "not found"));
|
||||||
|
if (data.doc)
|
||||||
|
tip.appendChild(document.createTextNode(" — " + data.doc));
|
||||||
|
if (data.url) {
|
||||||
|
tip.appendChild(document.createTextNode(" "));
|
||||||
|
var child = tip.appendChild(elt("a", null, "[docs]"));
|
||||||
|
child.href = data.url;
|
||||||
|
child.target = "_blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempTooltip(cm, tip, ts);
|
||||||
|
if (c) c();
|
||||||
|
}, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintaining argument hints
|
||||||
|
|
||||||
|
function updateArgHints(ts, cm) {
|
||||||
|
closeArgHints(ts);
|
||||||
|
|
||||||
|
if (cm.somethingSelected()) return;
|
||||||
|
var state = cm.getTokenAt(cm.getCursor()).state;
|
||||||
|
var inner = CodeMirror.innerMode(cm.getMode(), state);
|
||||||
|
if (inner.mode.name != "javascript") return;
|
||||||
|
var lex = inner.state.lexical;
|
||||||
|
if (lex.info != "call") return;
|
||||||
|
|
||||||
|
var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
|
||||||
|
for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
|
||||||
|
var str = cm.getLine(line), extra = 0;
|
||||||
|
for (var pos = 0;;) {
|
||||||
|
var tab = str.indexOf("\t", pos);
|
||||||
|
if (tab == -1) break;
|
||||||
|
extra += tabSize - (tab + extra) % tabSize - 1;
|
||||||
|
pos = tab + 1;
|
||||||
|
}
|
||||||
|
ch = lex.column - extra;
|
||||||
|
if (str.charAt(ch) == "(") {found = true; break;}
|
||||||
|
}
|
||||||
|
if (!found) return;
|
||||||
|
|
||||||
|
var start = Pos(line, ch);
|
||||||
|
var cache = ts.cachedArgHints;
|
||||||
|
if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
|
||||||
|
return showArgHints(ts, cm, argPos);
|
||||||
|
|
||||||
|
ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
|
||||||
|
if (error || !data.type || !(/^fn\(/).test(data.type)) return;
|
||||||
|
ts.cachedArgHints = {
|
||||||
|
start: start,
|
||||||
|
type: parseFnType(data.type),
|
||||||
|
name: data.exprName || data.name || "fn",
|
||||||
|
guess: data.guess,
|
||||||
|
doc: cm.getDoc()
|
||||||
|
};
|
||||||
|
showArgHints(ts, cm, argPos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showArgHints(ts, cm, pos) {
|
||||||
|
closeArgHints(ts);
|
||||||
|
|
||||||
|
var cache = ts.cachedArgHints, tp = cache.type;
|
||||||
|
var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
|
||||||
|
elt("span", cls + "fname", cache.name), "(");
|
||||||
|
for (var i = 0; i < tp.args.length; ++i) {
|
||||||
|
if (i) tip.appendChild(document.createTextNode(", "));
|
||||||
|
var arg = tp.args[i];
|
||||||
|
tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
|
||||||
|
if (arg.type != "?") {
|
||||||
|
tip.appendChild(document.createTextNode(":\u00a0"));
|
||||||
|
tip.appendChild(elt("span", cls + "type", arg.type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
|
||||||
|
if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
|
||||||
|
var place = cm.cursorCoords(null, "page");
|
||||||
|
var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm)
|
||||||
|
setTimeout(function() {
|
||||||
|
tooltip.clear = onEditorActivity(cm, function() {
|
||||||
|
if (ts.activeArgHints == tooltip) closeArgHints(ts) })
|
||||||
|
}, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFnType(text) {
|
||||||
|
var args = [], pos = 3;
|
||||||
|
|
||||||
|
function skipMatching(upto) {
|
||||||
|
var depth = 0, start = pos;
|
||||||
|
for (;;) {
|
||||||
|
var next = text.charAt(pos);
|
||||||
|
if (upto.test(next) && !depth) return text.slice(start, pos);
|
||||||
|
if (/[{\[\(]/.test(next)) ++depth;
|
||||||
|
else if (/[}\]\)]/.test(next)) --depth;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
if (text.charAt(pos) != ")") for (;;) {
|
||||||
|
var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
|
||||||
|
if (name) {
|
||||||
|
pos += name[0].length;
|
||||||
|
name = name[1];
|
||||||
|
}
|
||||||
|
args.push({name: name, type: skipMatching(/[\),]/)});
|
||||||
|
if (text.charAt(pos) == ")") break;
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rettype = text.slice(pos).match(/^\) -> (.*)$/);
|
||||||
|
|
||||||
|
return {args: args, rettype: rettype && rettype[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moving to the definition of something
|
||||||
|
|
||||||
|
function jumpToDef(ts, cm) {
|
||||||
|
function inner(varName) {
|
||||||
|
var req = {type: "definition", variable: varName || null};
|
||||||
|
var doc = findDoc(ts, cm.getDoc());
|
||||||
|
ts.server.request(buildRequest(ts, doc, req), function(error, data) {
|
||||||
|
if (error) return showError(ts, cm, error);
|
||||||
|
if (!data.file && data.url) { window.open(data.url); return; }
|
||||||
|
|
||||||
|
if (data.file) {
|
||||||
|
var localDoc = ts.docs[data.file], found;
|
||||||
|
if (localDoc && (found = findContext(localDoc.doc, data))) {
|
||||||
|
ts.jumpStack.push({file: doc.name,
|
||||||
|
start: cm.getCursor("from"),
|
||||||
|
end: cm.getCursor("to")});
|
||||||
|
moveTo(ts, doc, localDoc, found.start, found.end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showError(ts, cm, "Could not find a definition.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atInterestingExpression(cm))
|
||||||
|
dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
|
||||||
|
else
|
||||||
|
inner();
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpBack(ts, cm) {
|
||||||
|
var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
|
||||||
|
if (!doc) return;
|
||||||
|
moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveTo(ts, curDoc, doc, start, end) {
|
||||||
|
doc.doc.setSelection(start, end);
|
||||||
|
if (curDoc != doc && ts.options.switchToDoc) {
|
||||||
|
closeArgHints(ts);
|
||||||
|
ts.options.switchToDoc(doc.name, doc.doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The {line,ch} representation of positions makes this rather awkward.
|
||||||
|
function findContext(doc, data) {
|
||||||
|
var before = data.context.slice(0, data.contextOffset).split("\n");
|
||||||
|
var startLine = data.start.line - (before.length - 1);
|
||||||
|
var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
|
||||||
|
|
||||||
|
var text = doc.getLine(startLine).slice(start.ch);
|
||||||
|
for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
|
||||||
|
text += "\n" + doc.getLine(cur);
|
||||||
|
if (text.slice(0, data.context.length) == data.context) return data;
|
||||||
|
|
||||||
|
var cursor = doc.getSearchCursor(data.context, 0, false);
|
||||||
|
var nearest, nearestDist = Infinity;
|
||||||
|
while (cursor.findNext()) {
|
||||||
|
var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
|
||||||
|
if (!dist) dist = Math.abs(from.ch - start.ch);
|
||||||
|
if (dist < nearestDist) { nearest = from; nearestDist = dist; }
|
||||||
|
}
|
||||||
|
if (!nearest) return null;
|
||||||
|
|
||||||
|
if (before.length == 1)
|
||||||
|
nearest.ch += before[0].length;
|
||||||
|
else
|
||||||
|
nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
|
||||||
|
if (data.start.line == data.end.line)
|
||||||
|
var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
|
||||||
|
else
|
||||||
|
var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
|
||||||
|
return {start: nearest, end: end};
|
||||||
|
}
|
||||||
|
|
||||||
|
function atInterestingExpression(cm) {
|
||||||
|
var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
|
||||||
|
if (tok.start < pos.ch && tok.type == "comment") return false;
|
||||||
|
return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable renaming
|
||||||
|
|
||||||
|
function rename(ts, cm) {
|
||||||
|
var token = cm.getTokenAt(cm.getCursor());
|
||||||
|
if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
|
||||||
|
dialog(cm, "New name for " + token.string, function(newName) {
|
||||||
|
ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
|
||||||
|
if (error) return showError(ts, cm, error);
|
||||||
|
applyChanges(ts, data.changes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectName(ts, cm) {
|
||||||
|
var name = findDoc(ts, cm.doc).name;
|
||||||
|
ts.request(cm, {type: "refs"}, function(error, data) {
|
||||||
|
if (error) return showError(ts, cm, error);
|
||||||
|
var ranges = [], cur = 0;
|
||||||
|
var curPos = cm.getCursor();
|
||||||
|
for (var i = 0; i < data.refs.length; i++) {
|
||||||
|
var ref = data.refs[i];
|
||||||
|
if (ref.file == name) {
|
||||||
|
ranges.push({anchor: ref.start, head: ref.end});
|
||||||
|
if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)
|
||||||
|
cur = ranges.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cm.setSelections(ranges, cur);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextChangeOrig = 0;
|
||||||
|
function applyChanges(ts, changes) {
|
||||||
|
var perFile = Object.create(null);
|
||||||
|
for (var i = 0; i < changes.length; ++i) {
|
||||||
|
var ch = changes[i];
|
||||||
|
(perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
|
||||||
|
}
|
||||||
|
for (var file in perFile) {
|
||||||
|
var known = ts.docs[file], chs = perFile[file];;
|
||||||
|
if (!known) continue;
|
||||||
|
chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
|
||||||
|
var origin = "*rename" + (++nextChangeOrig);
|
||||||
|
for (var i = 0; i < chs.length; ++i) {
|
||||||
|
var ch = chs[i];
|
||||||
|
known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic request-building helper
|
||||||
|
|
||||||
|
function buildRequest(ts, doc, query, pos) {
|
||||||
|
var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
|
||||||
|
if (!allowFragments) delete query.fullDocs;
|
||||||
|
if (typeof query == "string") query = {type: query};
|
||||||
|
query.lineCharPositions = true;
|
||||||
|
if (query.end == null) {
|
||||||
|
query.end = pos || doc.doc.getCursor("end");
|
||||||
|
if (doc.doc.somethingSelected())
|
||||||
|
query.start = doc.doc.getCursor("start");
|
||||||
|
}
|
||||||
|
var startPos = query.start || query.end;
|
||||||
|
|
||||||
|
if (doc.changed) {
|
||||||
|
if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
|
||||||
|
doc.changed.to - doc.changed.from < 100 &&
|
||||||
|
doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
|
||||||
|
files.push(getFragmentAround(doc, startPos, query.end));
|
||||||
|
query.file = "#0";
|
||||||
|
var offsetLines = files[0].offsetLines;
|
||||||
|
if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
|
||||||
|
query.end = Pos(query.end.line - offsetLines, query.end.ch);
|
||||||
|
} else {
|
||||||
|
files.push({type: "full",
|
||||||
|
name: doc.name,
|
||||||
|
text: docValue(ts, doc)});
|
||||||
|
query.file = doc.name;
|
||||||
|
doc.changed = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query.file = doc.name;
|
||||||
|
}
|
||||||
|
for (var name in ts.docs) {
|
||||||
|
var cur = ts.docs[name];
|
||||||
|
if (cur.changed && cur != doc) {
|
||||||
|
files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
|
||||||
|
cur.changed = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {query: query, files: files};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFragmentAround(data, start, end) {
|
||||||
|
var doc = data.doc;
|
||||||
|
var minIndent = null, minLine = null, endLine, tabSize = 4;
|
||||||
|
for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
|
||||||
|
var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
|
||||||
|
if (fn < 0) continue;
|
||||||
|
var indent = CodeMirror.countColumn(line, null, tabSize);
|
||||||
|
if (minIndent != null && minIndent <= indent) continue;
|
||||||
|
minIndent = indent;
|
||||||
|
minLine = p;
|
||||||
|
}
|
||||||
|
if (minLine == null) minLine = min;
|
||||||
|
var max = Math.min(doc.lastLine(), end.line + 20);
|
||||||
|
if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
|
||||||
|
endLine = max;
|
||||||
|
else for (endLine = end.line + 1; endLine < max; ++endLine) {
|
||||||
|
var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
|
||||||
|
if (indent <= minIndent) break;
|
||||||
|
}
|
||||||
|
var from = Pos(minLine, 0);
|
||||||
|
|
||||||
|
return {type: "part",
|
||||||
|
name: data.name,
|
||||||
|
offsetLines: from.line,
|
||||||
|
text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic utilities
|
||||||
|
|
||||||
|
var cmpPos = CodeMirror.cmpPos;
|
||||||
|
|
||||||
|
function elt(tagname, cls /*, ... elts*/) {
|
||||||
|
var e = document.createElement(tagname);
|
||||||
|
if (cls) e.className = cls;
|
||||||
|
for (var i = 2; i < arguments.length; ++i) {
|
||||||
|
var elt = arguments[i];
|
||||||
|
if (typeof elt == "string") elt = document.createTextNode(elt);
|
||||||
|
e.appendChild(elt);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dialog(cm, text, f) {
|
||||||
|
if (cm.openDialog)
|
||||||
|
cm.openDialog(text + ": <input type=text>", f);
|
||||||
|
else
|
||||||
|
f(prompt(text, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tooltips
|
||||||
|
|
||||||
|
function tempTooltip(cm, content, ts) {
|
||||||
|
if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
|
||||||
|
var where = cm.cursorCoords();
|
||||||
|
var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm);
|
||||||
|
function maybeClear() {
|
||||||
|
old = true;
|
||||||
|
if (!mouseOnTip) clear();
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
cm.state.ternTooltip = null;
|
||||||
|
if (tip.parentNode) fadeOut(tip)
|
||||||
|
clearActivity()
|
||||||
|
}
|
||||||
|
var mouseOnTip = false, old = false;
|
||||||
|
CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
|
||||||
|
CodeMirror.on(tip, "mouseout", function(e) {
|
||||||
|
var related = e.relatedTarget || e.toElement
|
||||||
|
if (!related || !CodeMirror.contains(tip, related)) {
|
||||||
|
if (old) clear();
|
||||||
|
else mouseOnTip = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
|
||||||
|
var clearActivity = onEditorActivity(cm, clear)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditorActivity(cm, f) {
|
||||||
|
cm.on("cursorActivity", f)
|
||||||
|
cm.on("blur", f)
|
||||||
|
cm.on("scroll", f)
|
||||||
|
cm.on("setDoc", f)
|
||||||
|
return function() {
|
||||||
|
cm.off("cursorActivity", f)
|
||||||
|
cm.off("blur", f)
|
||||||
|
cm.off("scroll", f)
|
||||||
|
cm.off("setDoc", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeTooltip(x, y, content, cm) {
|
||||||
|
var node = elt("div", cls + "tooltip", content);
|
||||||
|
node.style.left = x + "px";
|
||||||
|
node.style.top = y + "px";
|
||||||
|
var container = ((cm.options || {}).hintOptions || {}).container || document.body;
|
||||||
|
container.appendChild(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(node) {
|
||||||
|
var p = node && node.parentNode;
|
||||||
|
if (p) p.removeChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fadeOut(tooltip) {
|
||||||
|
tooltip.style.opacity = "0";
|
||||||
|
setTimeout(function() { remove(tooltip); }, 1100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(ts, cm, msg) {
|
||||||
|
if (ts.options.showError)
|
||||||
|
ts.options.showError(cm, msg);
|
||||||
|
else
|
||||||
|
tempTooltip(cm, String(msg), ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeArgHints(ts) {
|
||||||
|
if (ts.activeArgHints) {
|
||||||
|
if (ts.activeArgHints.clear) ts.activeArgHints.clear()
|
||||||
|
remove(ts.activeArgHints)
|
||||||
|
ts.activeArgHints = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function docValue(ts, doc) {
|
||||||
|
var val = doc.doc.getValue();
|
||||||
|
if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worker wrapper
|
||||||
|
|
||||||
|
function WorkerServer(ts) {
|
||||||
|
var worker = ts.worker = new Worker(ts.options.workerScript);
|
||||||
|
worker.postMessage({type: "init",
|
||||||
|
defs: ts.options.defs,
|
||||||
|
plugins: ts.options.plugins,
|
||||||
|
scripts: ts.options.workerDeps});
|
||||||
|
var msgId = 0, pending = {};
|
||||||
|
|
||||||
|
function send(data, c) {
|
||||||
|
if (c) {
|
||||||
|
data.id = ++msgId;
|
||||||
|
pending[msgId] = c;
|
||||||
|
}
|
||||||
|
worker.postMessage(data);
|
||||||
|
}
|
||||||
|
worker.onmessage = function(e) {
|
||||||
|
var data = e.data;
|
||||||
|
if (data.type == "getFile") {
|
||||||
|
getFile(ts, data.name, function(err, text) {
|
||||||
|
send({type: "getFile", err: String(err), text: text, id: data.id});
|
||||||
|
});
|
||||||
|
} else if (data.type == "debug") {
|
||||||
|
window.console.log(data.message);
|
||||||
|
} else if (data.id && pending[data.id]) {
|
||||||
|
pending[data.id](data.err, data.body);
|
||||||
|
delete pending[data.id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
worker.onerror = function(e) {
|
||||||
|
for (var id in pending) pending[id](e);
|
||||||
|
pending = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
|
||||||
|
this.delFile = function(name) { send({type: "del", name: name}); };
|
||||||
|
this.request = function(body, c) { send({type: "req", body: body}, c); };
|
||||||
|
}
|
||||||
|
});
|
||||||
44
src/modules/_CodeMirror/addon/tern/worker.js
Normal file
44
src/modules/_CodeMirror/addon/tern/worker.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
// declare global: tern, server
|
||||||
|
|
||||||
|
var server;
|
||||||
|
|
||||||
|
this.onmessage = function(e) {
|
||||||
|
var data = e.data;
|
||||||
|
switch (data.type) {
|
||||||
|
case "init": return startServer(data.defs, data.plugins, data.scripts);
|
||||||
|
case "add": return server.addFile(data.name, data.text);
|
||||||
|
case "del": return server.delFile(data.name);
|
||||||
|
case "req": return server.request(data.body, function(err, reqData) {
|
||||||
|
postMessage({id: data.id, body: reqData, err: err && String(err)});
|
||||||
|
});
|
||||||
|
case "getFile":
|
||||||
|
var c = pending[data.id];
|
||||||
|
delete pending[data.id];
|
||||||
|
return c(data.err, data.text);
|
||||||
|
default: throw new Error("Unknown message type: " + data.type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var nextId = 0, pending = {};
|
||||||
|
function getFile(file, c) {
|
||||||
|
postMessage({type: "getFile", name: file, id: ++nextId});
|
||||||
|
pending[nextId] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startServer(defs, plugins, scripts) {
|
||||||
|
if (scripts) importScripts.apply(null, scripts);
|
||||||
|
|
||||||
|
server = new tern.Server({
|
||||||
|
getFile: getFile,
|
||||||
|
async: true,
|
||||||
|
defs: defs,
|
||||||
|
plugins: plugins
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.console = {
|
||||||
|
log: function(v) { postMessage({type: "debug", message: v}); }
|
||||||
|
};
|
||||||
159
src/modules/_CodeMirror/addon/wrap/hardwrap.js
Normal file
159
src/modules/_CodeMirror/addon/wrap/hardwrap.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
function findParagraph(cm, pos, options) {
|
||||||
|
var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart");
|
||||||
|
for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
|
||||||
|
var line = cm.getLine(start);
|
||||||
|
if (startRE && startRE.test(line)) break;
|
||||||
|
if (!/\S/.test(line)) { ++start; break; }
|
||||||
|
}
|
||||||
|
var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd");
|
||||||
|
for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
|
||||||
|
var line = cm.getLine(end);
|
||||||
|
if (endRE && endRE.test(line)) { ++end; break; }
|
||||||
|
if (!/\S/.test(line)) break;
|
||||||
|
}
|
||||||
|
return {from: start, to: end};
|
||||||
|
}
|
||||||
|
|
||||||
|
function findBreakPoint(text, column, wrapOn, killTrailingSpace, forceBreak) {
|
||||||
|
var at = column
|
||||||
|
while (at < text.length && text.charAt(at) == " ") at++
|
||||||
|
for (; at > 0; --at)
|
||||||
|
if (wrapOn.test(text.slice(at - 1, at + 1))) break;
|
||||||
|
|
||||||
|
if (at == 0 && !forceBreak) {
|
||||||
|
// didn't find a break point before column, in non-forceBreak mode try to
|
||||||
|
// find one after 'column'.
|
||||||
|
for (at = column + 1; at < text.length - 1; ++at) {
|
||||||
|
if (wrapOn.test(text.slice(at - 1, at + 1))) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var first = true;; first = false) {
|
||||||
|
var endOfText = at;
|
||||||
|
if (killTrailingSpace)
|
||||||
|
while (text.charAt(endOfText - 1) == " ") --endOfText;
|
||||||
|
if (endOfText == 0 && first) at = column;
|
||||||
|
else return {from: endOfText, to: at};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapRange(cm, from, to, options) {
|
||||||
|
from = cm.clipPos(from); to = cm.clipPos(to);
|
||||||
|
var column = options.column || 80;
|
||||||
|
var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/;
|
||||||
|
var forceBreak = options.forceBreak !== false;
|
||||||
|
var killTrailing = options.killTrailingSpace !== false;
|
||||||
|
var changes = [], curLine = "", curNo = from.line;
|
||||||
|
var lines = cm.getRange(from, to, false);
|
||||||
|
if (!lines.length) return null;
|
||||||
|
var leadingSpace = lines[0].match(/^[ \t]*/)[0];
|
||||||
|
if (leadingSpace.length >= column) column = leadingSpace.length + 1
|
||||||
|
|
||||||
|
for (var i = 0; i < lines.length; ++i) {
|
||||||
|
var text = lines[i], oldLen = curLine.length, spaceInserted = 0;
|
||||||
|
if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
|
||||||
|
curLine += " ";
|
||||||
|
spaceInserted = 1;
|
||||||
|
}
|
||||||
|
var spaceTrimmed = "";
|
||||||
|
if (i) {
|
||||||
|
spaceTrimmed = text.match(/^\s*/)[0];
|
||||||
|
text = text.slice(spaceTrimmed.length);
|
||||||
|
}
|
||||||
|
curLine += text;
|
||||||
|
if (i) {
|
||||||
|
var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed &&
|
||||||
|
findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);
|
||||||
|
// If this isn't broken, or is broken at a different point, remove old break
|
||||||
|
if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {
|
||||||
|
changes.push({text: [spaceInserted ? " " : ""],
|
||||||
|
from: Pos(curNo, oldLen),
|
||||||
|
to: Pos(curNo + 1, spaceTrimmed.length)});
|
||||||
|
} else {
|
||||||
|
curLine = leadingSpace + text;
|
||||||
|
++curNo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (curLine.length > column) {
|
||||||
|
var bp = findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);
|
||||||
|
if (bp.from != bp.to || forceBreak) {
|
||||||
|
changes.push({text: ["", leadingSpace],
|
||||||
|
from: Pos(curNo, bp.from),
|
||||||
|
to: Pos(curNo, bp.to)});
|
||||||
|
curLine = leadingSpace + curLine.slice(bp.to);
|
||||||
|
++curNo;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changes.length) cm.operation(function() {
|
||||||
|
for (var i = 0; i < changes.length; ++i) {
|
||||||
|
var change = changes[i];
|
||||||
|
if (change.text || CodeMirror.cmpPos(change.from, change.to))
|
||||||
|
cm.replaceRange(change.text, change.from, change.to);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("wrapParagraph", function(pos, options) {
|
||||||
|
options = options || {};
|
||||||
|
if (!pos) pos = this.getCursor();
|
||||||
|
var para = findParagraph(this, pos, options);
|
||||||
|
return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.commands.wrapLines = function(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
var ranges = cm.listSelections(), at = cm.lastLine() + 1;
|
||||||
|
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||||
|
var range = ranges[i], span;
|
||||||
|
if (range.empty()) {
|
||||||
|
var para = findParagraph(cm, range.head, {});
|
||||||
|
span = {from: Pos(para.from, 0), to: Pos(para.to - 1)};
|
||||||
|
} else {
|
||||||
|
span = {from: range.from(), to: range.to()};
|
||||||
|
}
|
||||||
|
if (span.to.line >= at) continue;
|
||||||
|
at = span.from.line;
|
||||||
|
wrapRange(cm, span.from, span.to, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("wrapRange", function(from, to, options) {
|
||||||
|
return wrapRange(this, from, to, options || {});
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) {
|
||||||
|
options = options || {};
|
||||||
|
var cm = this, paras = [];
|
||||||
|
for (var line = from.line; line <= to.line;) {
|
||||||
|
var para = findParagraph(cm, Pos(line, 0), options);
|
||||||
|
paras.push(para);
|
||||||
|
line = para.to;
|
||||||
|
}
|
||||||
|
var madeChange = false;
|
||||||
|
if (paras.length) cm.operation(function() {
|
||||||
|
for (var i = paras.length - 1; i >= 0; --i)
|
||||||
|
madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options);
|
||||||
|
});
|
||||||
|
return madeChange;
|
||||||
|
});
|
||||||
|
});
|
||||||
70
src/modules/_CodeMirror/cm-game-settings.js
Normal file
70
src/modules/_CodeMirror/cm-game-settings.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Defines a set of configuartion options for CodeMirror editors
|
||||||
|
*/
|
||||||
|
Hooks.once("init", function () {
|
||||||
|
const namespace ="_CodeMirror";
|
||||||
|
const settings = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a single game setting for this module.
|
||||||
|
*
|
||||||
|
* The settings is registered with the module's namespace,
|
||||||
|
* and the name and hint are assigned translated values
|
||||||
|
* based on the setting name.
|
||||||
|
*
|
||||||
|
* All the settings are world scoped.
|
||||||
|
*
|
||||||
|
* @param {string} name - The name of the setting, also used for .
|
||||||
|
* @param {type} type - The data type of the setting value.
|
||||||
|
* @param {Number|Boolean|String} def - The default value of the setting.
|
||||||
|
* @param {Boolean} include - Whether or not to include this setting in the settings array.
|
||||||
|
*/
|
||||||
|
const register = function(name, type, def, include) {
|
||||||
|
game.settings.register(namespace, name, {
|
||||||
|
name: game.i18n.localize(`${namespace}.settings.${name}.name`),
|
||||||
|
hint: game.i18n.localize(`${namespace}.settings.${name}.hint`),
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
type: type,
|
||||||
|
default: def
|
||||||
|
});
|
||||||
|
if (include) settings.push(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a getter to CodeMirror that returns an object containing all the settings
|
||||||
|
* from the settings array. These can be passed directly to the CM initialization.
|
||||||
|
*/
|
||||||
|
Object.defineProperty(CodeMirror, "userSettings", {
|
||||||
|
get: function() { return Object.fromEntries(
|
||||||
|
settings.map(name => [name, game.settings.get(namespace, name)])
|
||||||
|
)}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
register("macroEditor" , Boolean, false, false);
|
||||||
|
|
||||||
|
register("indentUnit" , Number , 4, true);
|
||||||
|
register("smartIndent" , Boolean, true, true);
|
||||||
|
register("tabSize" , Number , 4, true);
|
||||||
|
register("indentWithTabs", Boolean, true, true);
|
||||||
|
register("smartIndent" , Boolean, true, true);
|
||||||
|
register("lineWrapping" , Boolean, false, true);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the macro config renders, if the setting to replace the editor is enabled,
|
||||||
|
* initialize an editor there.
|
||||||
|
*/
|
||||||
|
Hooks.on("renderMacroConfig", (application, html, data) => {
|
||||||
|
if (!game.settings.get(namespace, "macroEditor")) return;
|
||||||
|
|
||||||
|
CodeMirror.fromTextArea(html.find("textarea[name=command]")[0], {
|
||||||
|
mode: "javascript",
|
||||||
|
...CodeMirror.userSettings,
|
||||||
|
lineNumbers: true,
|
||||||
|
inputStyle: "contenteditable",
|
||||||
|
autofocus: true
|
||||||
|
}).on("change", (instance) => instance.save());
|
||||||
|
});
|
||||||
|
});
|
||||||
16
src/modules/_CodeMirror/codemirror-vtt.css
Normal file
16
src/modules/_CodeMirror/codemirror-vtt.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.CodeMirror {
|
||||||
|
cursor: text;
|
||||||
|
background: #ffffff33;
|
||||||
|
box-shadow: 0 0 12px -6px #00000070 inset;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
background-image: url(../../ui/parchment.jpg);
|
||||||
|
background-color: hsl(0deg 0% 0% / 2%);
|
||||||
|
background-blend-mode: darken;
|
||||||
|
box-shadow: 0 0 5px 0px #0000005c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fopr when Macro editor option is enabled */
|
||||||
|
.window-app.macro-sheet .CodeMirror {
|
||||||
|
height: 14em;
|
||||||
|
}
|
||||||
88
src/modules/_CodeMirror/demo/activeline.html
Normal file
88
src/modules/_CodeMirror/demo/activeline.html
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<title>CodeMirror: Active Line Demo</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<link rel=stylesheet href="../doc/docs.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../lib/codemirror.css">
|
||||||
|
<script src="../lib/codemirror.js"></script>
|
||||||
|
<script src="../mode/xml/xml.js"></script>
|
||||||
|
<script src="../addon/selection/active-line.js"></script>
|
||||||
|
<style>
|
||||||
|
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
|
||||||
|
</style>
|
||||||
|
<div id=nav>
|
||||||
|
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="../index.html">Home</a>
|
||||||
|
<li><a href="../doc/manual.html">Manual</a>
|
||||||
|
<li><a href="https://github.com/codemirror/codemirror">Code</a>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a class=active href="#">Active Line</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Active Line Demo</h2>
|
||||||
|
<form><textarea id="code" name="code">
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
|
||||||
|
xmlns:georss="http://www.georss.org/georss"
|
||||||
|
xmlns:twitter="http://api.twitter.com">
|
||||||
|
<channel>
|
||||||
|
<title>Twitter / codemirror</title>
|
||||||
|
<link>http://twitter.com/codemirror</link>
|
||||||
|
<atom:link type="application/rss+xml"
|
||||||
|
href="http://twitter.com/statuses/user_timeline/242283288.rss" rel="self"/>
|
||||||
|
<description>Twitter updates from CodeMirror / codemirror.</description>
|
||||||
|
<language>en-us</language>
|
||||||
|
<ttl>40</ttl>
|
||||||
|
<item>
|
||||||
|
<title>codemirror: http://cloud-ide.com — they're springing up like mushrooms. This one
|
||||||
|
uses CodeMirror as its editor.</title>
|
||||||
|
<description>codemirror: http://cloud-ide.com — they're springing up like mushrooms. This
|
||||||
|
one uses CodeMirror as its editor.</description>
|
||||||
|
<pubDate>Thu, 17 Mar 2011 23:34:47 +0000</pubDate>
|
||||||
|
<guid>http://twitter.com/codemirror/statuses/48527733722058752</guid>
|
||||||
|
<link>http://twitter.com/codemirror/statuses/48527733722058752</link>
|
||||||
|
<twitter:source>web</twitter:source>
|
||||||
|
<twitter:place/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title>codemirror: Posted a description of the CodeMirror 2 internals at
|
||||||
|
https://codemirror.net/2/internals.html</title>
|
||||||
|
<description>codemirror: Posted a description of the CodeMirror 2 internals at
|
||||||
|
https://codemirror.net/2/internals.html</description>
|
||||||
|
<pubDate>Wed, 02 Mar 2011 12:15:09 +0000</pubDate>
|
||||||
|
<guid>http://twitter.com/codemirror/statuses/42920879788789760</guid>
|
||||||
|
<link>http://twitter.com/codemirror/statuses/42920879788789760</link>
|
||||||
|
<twitter:source>web</twitter:source>
|
||||||
|
<twitter:place/>
|
||||||
|
</item>
|
||||||
|
</channel>
|
||||||
|
</rss></textarea></form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var nonEmpty = false;
|
||||||
|
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||||
|
mode: "application/xml",
|
||||||
|
styleActiveLine: true,
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleSelProp() {
|
||||||
|
nonEmpty = !nonEmpty;
|
||||||
|
editor.setOption("styleActiveLine", {nonEmpty: nonEmpty});
|
||||||
|
var label = nonEmpty ? 'Disable nonEmpty option' : 'Enable nonEmpty option';
|
||||||
|
document.getElementById('toggleButton').innerText = label;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>Styling the current cursor line.</p>
|
||||||
|
|
||||||
|
<button onclick="toggleSelProp()" id="toggleButton">Enable <code>nonEmpty</code> option</button>
|
||||||
|
|
||||||
|
</article>
|
||||||
79
src/modules/_CodeMirror/demo/anywordhint.html
Normal file
79
src/modules/_CodeMirror/demo/anywordhint.html
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<title>CodeMirror: Any Word Completion Demo</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<link rel=stylesheet href="../doc/docs.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../lib/codemirror.css">
|
||||||
|
<link rel="stylesheet" href="../addon/hint/show-hint.css">
|
||||||
|
<script src="../lib/codemirror.js"></script>
|
||||||
|
<script src="../addon/hint/show-hint.js"></script>
|
||||||
|
<script src="../addon/hint/anyword-hint.js"></script>
|
||||||
|
<script src="../mode/javascript/javascript.js"></script>
|
||||||
|
<div id=nav>
|
||||||
|
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="../index.html">Home</a>
|
||||||
|
<li><a href="../doc/manual.html">Manual</a>
|
||||||
|
<li><a href="https://github.com/codemirror/codemirror">Code</a>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a class=active href="#">Any Word Completion</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Any Word Completion Demo</h2>
|
||||||
|
<form><textarea id="code" name="code">
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var WORD = /[\w$]+/g, RANGE = 500;
|
||||||
|
|
||||||
|
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
|
||||||
|
var word = options && options.word || WORD;
|
||||||
|
var range = options && options.range || RANGE;
|
||||||
|
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
|
||||||
|
var start = cur.ch, end = start;
|
||||||
|
while (end < curLine.length && word.test(curLine.charAt(end))) ++end;
|
||||||
|
while (start && word.test(curLine.charAt(start - 1))) --start;
|
||||||
|
var curWord = start != end && curLine.slice(start, end);
|
||||||
|
|
||||||
|
var list = [], seen = {};
|
||||||
|
function scan(dir) {
|
||||||
|
var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
|
||||||
|
for (; line != end; line += dir) {
|
||||||
|
var text = editor.getLine(line), m;
|
||||||
|
word.lastIndex = 0;
|
||||||
|
while (m = word.exec(text)) {
|
||||||
|
if ((!curWord || m[0].indexOf(curWord) == 0) && !seen.hasOwnProperty(m[0])) {
|
||||||
|
seen[m[0]] = true;
|
||||||
|
list.push(m[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scan(-1);
|
||||||
|
scan(1);
|
||||||
|
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</textarea></form>
|
||||||
|
|
||||||
|
<p>Press <strong>ctrl-space</strong> to activate autocompletion. The
|
||||||
|
completion uses
|
||||||
|
the <a href="../doc/manual.html#addon_anyword-hint">anyword-hint.js</a>
|
||||||
|
module, which simply looks at nearby words in the buffer and completes
|
||||||
|
to those.</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
CodeMirror.commands.autocomplete = function(cm) {
|
||||||
|
cm.showHint({hint: CodeMirror.hint.anyword});
|
||||||
|
}
|
||||||
|
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||||
|
lineNumbers: true,
|
||||||
|
extraKeys: {"Ctrl-Space": "autocomplete"}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</article>
|
||||||
106
src/modules/_CodeMirror/demo/bidi.html
Normal file
106
src/modules/_CodeMirror/demo/bidi.html
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<title>CodeMirror: Bi-directional Text Demo</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<link rel=stylesheet href="../doc/docs.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../lib/codemirror.css">
|
||||||
|
<script src="../lib/codemirror.js"></script>
|
||||||
|
<script src="../mode/xml/xml.js"></script>
|
||||||
|
<style>
|
||||||
|
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
|
||||||
|
fieldset {border: none}
|
||||||
|
</style>
|
||||||
|
<div id=nav>
|
||||||
|
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="../index.html">Home</a>
|
||||||
|
<li><a href="../doc/manual.html">Manual</a>
|
||||||
|
<li><a href="https://github.com/codemirror/codemirror">Code</a>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a class=active href="#">Bi-directional Text</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Bi-directional Text Demo</h2>
|
||||||
|
<form><textarea id="code" name="code"><!-- Piece of the CodeMirror manual, 'translated' into Arabic by Google Translate -->
|
||||||
|
<!-- قطعة من دليل CodeMirror، "ترجم" إلى العربية بواسطة جوجل ترجمة -->
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt id=option_value><code>value (string or Doc)</code></dt>
|
||||||
|
<dd>قيمة البداية المحرر. يمكن أن تكون سلسلة، أو. كائن مستند.</dd>
|
||||||
|
<dt id=option_mode><code>mode (string or object)</code></dt>
|
||||||
|
<dd>وضع الاستخدام. عندما لا تعطى، وهذا الافتراضي إلى الطريقة الاولى
|
||||||
|
التي تم تحميلها. قد يكون من سلسلة، والتي إما أسماء أو ببساطة هو وضع
|
||||||
|
MIME نوع المرتبطة اسطة. بدلا من ذلك، قد يكون من كائن يحتوي على
|
||||||
|
خيارات التكوين لواسطة، مع <code>name</code> الخاصية التي وضع أسماء
|
||||||
|
(على سبيل المثال <code>{name: "javascript", json: true}</code>).
|
||||||
|
صفحات التجريبي لكل وضع تحتوي على معلومات حول ما معلمات تكوين وضع
|
||||||
|
يدعمها. يمكنك أن تطلب CodeMirror التي تم تعريفها طرق وأنواع MIME
|
||||||
|
الكشف على <code>CodeMirror.modes</code>
|
||||||
|
و <code>CodeMirror.mimeModes</code> الكائنات. وضع خرائط الأسماء
|
||||||
|
الأولى لمنشئات الخاصة بهم، وخرائط لأنواع MIME 2 المواصفات
|
||||||
|
واسطة.</dd>
|
||||||
|
<dt id=option_theme><code>theme (string)</code></dt>
|
||||||
|
<dd>موضوع لنمط المحرر مع. يجب عليك التأكد من الملف CSS تحديد
|
||||||
|
المقابلة <code>.cm-s-[name]</code> يتم تحميل أنماط (انظر
|
||||||
|
<a href="../theme/"><code>theme</code></a> الدليل في التوزيع).
|
||||||
|
الافتراضي هو <code>"default"</code> ، والتي تم تضمينها في
|
||||||
|
الألوان <code>codemirror.css</code>. فمن الممكن استخدام فئات متعددة
|
||||||
|
في تطبيق السمات مرة واحدة على سبيل المثال <code>"foo bar"</code>
|
||||||
|
سيتم تعيين كل من <code>cm-s-foo</code> و <code>cm-s-bar</code>
|
||||||
|
الطبقات إلى المحرر.</dd>
|
||||||
|
</dl>
|
||||||
|
</textarea>
|
||||||
|
<fieldset>
|
||||||
|
Editor default direction:
|
||||||
|
<input type="radio" id="ltr" name="direction"/><label for="ltr">LTR</label>
|
||||||
|
<input type="radio" id="rtl" name="direction"/><label for="rtl">RTL</label>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
HTML document direction:
|
||||||
|
<input type="radio" id="htmlltr" name="htmldirection"/><label for="htmlltr">LTR</label>
|
||||||
|
<input type="radio" id="htmlrtl" name="htmldirection"/><label for="htmlrtl">RTL</label>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<input type="checkbox" id="rtlMoveVisually"/><label for="rtlMoveVisually">Use visual order for arrow key movement.</label>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||||
|
mode: "text/html",
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true,
|
||||||
|
direction: "rtl"
|
||||||
|
});
|
||||||
|
|
||||||
|
var dirRadios = {ltr: document.getElementById("ltr"),
|
||||||
|
rtl: document.getElementById("rtl")};
|
||||||
|
dirRadios[editor.getOption("direction")].checked = true;
|
||||||
|
dirRadios["rtl"].onchange = dirRadios["ltr"].onchange = function() {
|
||||||
|
editor.setOption("direction", dirRadios["rtl"].checked ? "rtl" : "ltr");
|
||||||
|
};
|
||||||
|
|
||||||
|
var HtmlDirRadios = {ltr: document.getElementById("htmlltr"),
|
||||||
|
rtl: document.getElementById("htmlrtl")};
|
||||||
|
HtmlDirRadios["ltr"].checked = true;
|
||||||
|
HtmlDirRadios["rtl"].onchange = HtmlDirRadios["ltr"].onchange = function() {
|
||||||
|
document.dir = (HtmlDirRadios["rtl"].checked ? "rtl" : "ltr");
|
||||||
|
};
|
||||||
|
|
||||||
|
var moveCheckbox = document.getElementById("rtlMoveVisually");
|
||||||
|
moveCheckbox.checked = editor.getOption("rtlMoveVisually");
|
||||||
|
moveCheckbox.onchange = function() {
|
||||||
|
editor.setOption("rtlMoveVisually", moveCheckbox.checked);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>Demonstration of bi-directional text support. See
|
||||||
|
the <a href="http://marijnhaverbeke.nl/blog/cursor-in-bidi-text.html">related
|
||||||
|
blog post</a> for more background.</p>
|
||||||
|
|
||||||
|
</article>
|
||||||
83
src/modules/_CodeMirror/demo/btree.html
Normal file
83
src/modules/_CodeMirror/demo/btree.html
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<title>CodeMirror: B-Tree visualization</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<link rel=stylesheet href="../doc/docs.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../lib/codemirror.css">
|
||||||
|
<script src="../lib/codemirror.js"></script>
|
||||||
|
<style>
|
||||||
|
.lineblock { display: inline-block; margin: 1px; height: 5px; }
|
||||||
|
.CodeMirror {border: 1px solid #aaa; height: 400px}
|
||||||
|
</style>
|
||||||
|
<div id=nav>
|
||||||
|
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="../index.html">Home</a>
|
||||||
|
<li><a href="../doc/manual.html">Manual</a>
|
||||||
|
<li><a href="https://github.com/codemirror/codemirror">Code</a>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a class=active href="#">B-Tree visualization</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>B-Tree visualization</h2>
|
||||||
|
<form><textarea id="code" name="code">type here, see a summary of the document b-tree below</textarea></form>
|
||||||
|
<div style="display: inline-block; height: 402px; overflow-y: auto" id="output"></div>
|
||||||
|
|
||||||
|
<script id="me">
|
||||||
|
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true
|
||||||
|
});
|
||||||
|
var updateTimeout;
|
||||||
|
editor.on("change", function(cm) {
|
||||||
|
clearTimeout(updateTimeout);
|
||||||
|
updateTimeout = setTimeout(updateVisual, 200);
|
||||||
|
});
|
||||||
|
updateVisual();
|
||||||
|
|
||||||
|
function updateVisual() {
|
||||||
|
var out = document.getElementById("output");
|
||||||
|
out.innerHTML = "";
|
||||||
|
|
||||||
|
function drawTree(out, node) {
|
||||||
|
if (node.lines) {
|
||||||
|
out.appendChild(document.createElement("div")).innerHTML =
|
||||||
|
"<b>leaf</b>: " + node.lines.length + " lines, " + Math.round(node.height) + " px";
|
||||||
|
var lines = out.appendChild(document.createElement("div"));
|
||||||
|
lines.style.lineHeight = "6px"; lines.style.marginLeft = "10px";
|
||||||
|
for (var i = 0; i < node.lines.length; ++i) {
|
||||||
|
var line = node.lines[i], lineElt = lines.appendChild(document.createElement("div"));
|
||||||
|
lineElt.className = "lineblock";
|
||||||
|
var gray = Math.min(line.text.length * 3, 230), col = gray.toString(16);
|
||||||
|
if (col.length == 1) col = "0" + col;
|
||||||
|
lineElt.style.background = "#" + col + col + col;
|
||||||
|
lineElt.style.width = Math.max(Math.round(line.height / 3), 1) + "px";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.appendChild(document.createElement("div")).innerHTML =
|
||||||
|
"<b>node</b>: " + node.size + " lines, " + Math.round(node.height) + " px";
|
||||||
|
var sub = out.appendChild(document.createElement("div"));
|
||||||
|
sub.style.paddingLeft = "20px";
|
||||||
|
for (var i = 0; i < node.children.length; ++i)
|
||||||
|
drawTree(sub, node.children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawTree(out, editor.getDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillEditor() {
|
||||||
|
var sc = document.getElementById("me");
|
||||||
|
var doc = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\s*/, "") + "\n";
|
||||||
|
doc += doc; doc += doc; doc += doc; doc += doc; doc += doc; doc += doc;
|
||||||
|
editor.setValue(doc);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p><button onclick="fillEditor()">Add a lot of content</button></p>
|
||||||
|
|
||||||
|
</article>
|
||||||
109
src/modules/_CodeMirror/demo/buffers.html
Normal file
109
src/modules/_CodeMirror/demo/buffers.html
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<title>CodeMirror: Multiple Buffer & Split View Demo</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<link rel=stylesheet href="../doc/docs.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../lib/codemirror.css">
|
||||||
|
<script src="../lib/codemirror.js"></script>
|
||||||
|
<script src="../mode/javascript/javascript.js"></script>
|
||||||
|
<script src="../mode/css/css.js"></script>
|
||||||
|
<style id=style>
|
||||||
|
.CodeMirror {border: 1px solid black; height: 250px;}
|
||||||
|
</style>
|
||||||
|
<div id=nav>
|
||||||
|
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="../index.html">Home</a>
|
||||||
|
<li><a href="../doc/manual.html">Manual</a>
|
||||||
|
<li><a href="https://github.com/codemirror/codemirror">Code</a>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a class=active href="#">Multiple Buffer & Split View</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Multiple Buffer & Split View Demo</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<div id=code_top></div>
|
||||||
|
<div>
|
||||||
|
Select buffer: <select id=buffers_top></select>
|
||||||
|
<button onclick="newBuf('top')">New buffer</button>
|
||||||
|
</div>
|
||||||
|
<div id=code_bot></div>
|
||||||
|
<div>
|
||||||
|
Select buffer: <select id=buffers_bot></select>
|
||||||
|
<button onclick="newBuf('bot')">New buffer</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script id=script>
|
||||||
|
var sel_top = document.getElementById("buffers_top");
|
||||||
|
CodeMirror.on(sel_top, "change", function() {
|
||||||
|
selectBuffer(ed_top, sel_top.options[sel_top.selectedIndex].value);
|
||||||
|
});
|
||||||
|
|
||||||
|
var sel_bot = document.getElementById("buffers_bot");
|
||||||
|
CodeMirror.on(sel_bot, "change", function() {
|
||||||
|
selectBuffer(ed_bot, sel_bot.options[sel_bot.selectedIndex].value);
|
||||||
|
});
|
||||||
|
|
||||||
|
var buffers = {};
|
||||||
|
|
||||||
|
function openBuffer(name, text, mode) {
|
||||||
|
buffers[name] = CodeMirror.Doc(text, mode);
|
||||||
|
var opt = document.createElement("option");
|
||||||
|
opt.appendChild(document.createTextNode(name));
|
||||||
|
sel_top.appendChild(opt);
|
||||||
|
sel_bot.appendChild(opt.cloneNode(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
function newBuf(where) {
|
||||||
|
var name = prompt("Name for the buffer", "*scratch*");
|
||||||
|
if (name == null) return;
|
||||||
|
if (buffers.hasOwnProperty(name)) {
|
||||||
|
alert("There's already a buffer by that name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openBuffer(name, "", "javascript");
|
||||||
|
selectBuffer(where == "top" ? ed_top : ed_bot, name);
|
||||||
|
var sel = where == "top" ? sel_top : sel_bot;
|
||||||
|
sel.value = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectBuffer(editor, name) {
|
||||||
|
var buf = buffers[name];
|
||||||
|
if (buf.getEditor()) buf = buf.linkedDoc({sharedHist: true});
|
||||||
|
var old = editor.swapDoc(buf);
|
||||||
|
var linked = old.iterLinkedDocs(function(doc) {linked = doc;});
|
||||||
|
if (linked) {
|
||||||
|
// Make sure the document in buffers is the one the other view is looking at
|
||||||
|
for (var name in buffers) if (buffers[name] == old) buffers[name] = linked;
|
||||||
|
old.unlinkDoc(linked);
|
||||||
|
}
|
||||||
|
editor.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeContent(id) {
|
||||||
|
var node = document.getElementById(id), val = node.textContent || node.innerText;
|
||||||
|
val = val.slice(val.match(/^\s*/)[0].length, val.length - val.match(/\s*$/)[0].length) + "\n";
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
openBuffer("js", nodeContent("script"), "javascript");
|
||||||
|
openBuffer("css", nodeContent("style"), "css");
|
||||||
|
|
||||||
|
var ed_top = CodeMirror(document.getElementById("code_top"), {lineNumbers: true});
|
||||||
|
selectBuffer(ed_top, "js");
|
||||||
|
var ed_bot = CodeMirror(document.getElementById("code_bot"), {lineNumbers: true});
|
||||||
|
selectBuffer(ed_bot, "js");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>Demonstration of
|
||||||
|
using <a href="../doc/manual.html#linkedDoc">linked documents</a>
|
||||||
|
to provide a split view on a document, and
|
||||||
|
using <a href="../doc/manual.html#swapDoc"><code>swapDoc</code></a>
|
||||||
|
to use a single editor to display multiple documents.</p>
|
||||||
|
|
||||||
|
</article>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user