##// END OF EJS Templates
Fixed bug where properties couldn't be set on model till model was shown
Fixed bug where properties couldn't be set on model till model was shown

File last commit:

r14033:aeffe3be
r14246:bd79e955
Show More
utils.js
504 lines | 17.8 KiB | application/javascript | JavascriptLexer
//----------------------------------------------------------------------------
// Copyright (C) 2008-2012 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
// Utilities
//============================================================================
IPython.namespace('IPython.utils');
IPython.utils = (function (IPython) {
"use strict";
//============================================================================
// Cross-browser RegEx Split
//============================================================================
// This code has been MODIFIED from the code licensed below to not replace the
// default browser split. The license is reproduced here.
// see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
/*!
* Cross-Browser Split 1.1.1
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
* Available under the MIT License
* ECMAScript compliant, uniform cross-browser split method
*/
/**
* Splits a string into an array of strings using a regex or string
* separator. Matches of the separator are not included in the result array.
* However, if `separator` is a regex that contains capturing groups,
* backreferences are spliced into the result each time `separator` is
* matched. Fixes browser bugs compared to the native
* `String.prototype.split` and can be used reliably cross-browser.
* @param {String} str String to split.
* @param {RegExp|String} separator Regex or string to use for separating
* the string.
* @param {Number} [limit] Maximum number of items to include in the result
* array.
* @returns {Array} Array of substrings.
* @example
*
* // Basic use
* regex_split('a b c d', ' ');
* // -> ['a', 'b', 'c', 'd']
*
* // With limit
* regex_split('a b c d', ' ', 2);
* // -> ['a', 'b']
*
* // Backreferences in result array
* regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
* // -> ['..', 'word', '1', ' ', 'word', '2', '..']
*/
var regex_split = function (str, separator, limit) {
// If `separator` is not a regex, use `split`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return split.call(str, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? "i" : "") +
(separator.multiline ? "m" : "") +
(separator.extended ? "x" : "") + // Proposed for ES6
(separator.sticky ? "y" : ""), // Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator = new RegExp(separator.source, flags + "g"),
separator2, match, lastIndex, lastLength;
str += ""; // Type-convert
var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
limit = typeof(limit) === "undefined" ?
-1 >>> 0 : // Math.pow(2, 32) - 1
limit >>> 0; // ToUint32(limit)
while (match = separator.exec(str)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (typeof(arguments[i]) === "undefined") {
match[i] = undefined;
}
}
});
}
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
//============================================================================
// End contributed Cross-browser RegEx Split
//============================================================================
var uuid = function () {
// http://www.ietf.org/rfc/rfc4122.txt
var s = [];
var hexDigits = "0123456789ABCDEF";
for (var i = 0; i < 32; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
var uuid = s.join("");
return uuid;
};
//Fix raw text to parse correctly in crazy XML
function xmlencode(string) {
return string.replace(/\&/g,'&'+'amp;')
.replace(/</g,'&'+'lt;')
.replace(/>/g,'&'+'gt;')
.replace(/\'/g,'&'+'apos;')
.replace(/\"/g,'&'+'quot;')
.replace(/`/g,'&'+'#96;');
}
//Map from terminal commands to CSS classes
var ansi_colormap = {
"01":"ansibold",
"30":"ansiblack",
"31":"ansired",
"32":"ansigreen",
"33":"ansiyellow",
"34":"ansiblue",
"35":"ansipurple",
"36":"ansicyan",
"37":"ansigray",
"40":"ansibgblack",
"41":"ansibgred",
"42":"ansibggreen",
"43":"ansibgyellow",
"44":"ansibgblue",
"45":"ansibgpurple",
"46":"ansibgcyan",
"47":"ansibggray"
};
function _process_numbers(attrs, numbers) {
// process ansi escapes
var n = numbers.shift();
if (ansi_colormap[n]) {
if ( ! attrs["class"] ) {
attrs["class"] = ansi_colormap[n];
} else {
attrs["class"] += " " + ansi_colormap[n];
}
} else if (n == "38" || n == "48") {
// VT100 256 color or 24 bit RGB
if (numbers.length < 2) {
console.log("Not enough fields for VT100 color", numbers);
return;
}
var index_or_rgb = numbers.shift();
var r,g,b;
if (index_or_rgb == "5") {
// 256 color
var idx = parseInt(numbers.shift());
if (idx < 16) {
// indexed ANSI
// ignore bright / non-bright distinction
idx = idx % 8;
var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
if ( ! attrs["class"] ) {
attrs["class"] = ansiclass;
} else {
attrs["class"] += " " + ansiclass;
}
return;
} else if (idx < 232) {
// 216 color 6x6x6 RGB
idx = idx - 16;
b = idx % 6;
g = Math.floor(idx / 6) % 6;
r = Math.floor(idx / 36) % 6;
// convert to rgb
r = (r * 51);
g = (g * 51);
b = (b * 51);
} else {
// grayscale
idx = idx - 231;
// it's 1-24 and should *not* include black or white,
// so a 26 point scale
r = g = b = Math.floor(idx * 256 / 26);
}
} else if (index_or_rgb == "2") {
// Simple 24 bit RGB
if (numbers.length > 3) {
console.log("Not enough fields for RGB", numbers);
return;
}
r = numbers.shift();
g = numbers.shift();
b = numbers.shift();
} else {
console.log("unrecognized control", numbers);
return;
}
if (r !== undefined) {
// apply the rgb color
var line;
if (n == "38") {
line = "color: ";
} else {
line = "background-color: ";
}
line = line + "rgb(" + r + "," + g + "," + b + ");"
if ( !attrs["style"] ) {
attrs["style"] = line;
} else {
attrs["style"] += " " + line;
}
}
}
}
function ansispan(str) {
// ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
// regular ansi escapes (using the table above)
return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
if (!pattern) {
// [(01|22|39|)m close spans
return "</span>";
}
// consume sequence of color escapes
var numbers = pattern.match(/\d+/g);
var attrs = {};
while (numbers.length > 0) {
_process_numbers(attrs, numbers);
}
var span = "<span ";
for (var attr in attrs) {
var value = attrs[attr];
span = span + " " + attr + '="' + attrs[attr] + '"';
}
return span + ">";
});
};
// Transform ANSI color escape codes into HTML <span> tags with css
// classes listed in the above ansi_colormap object. The actual color used
// are set in the css file.
function fixConsole(txt) {
txt = xmlencode(txt);
var re = /\033\[([\dA-Fa-f;]*?)m/;
var opened = false;
var cmds = [];
var opener = "";
var closer = "";
// Strip all ANSI codes that are not color related. Matches
// all ANSI codes that do not end with "m".
var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
txt = txt.replace(ignored_re, "");
// color ansi codes
txt = ansispan(txt);
return txt;
}
// Remove chunks that should be overridden by the effect of
// carriage return characters
function fixCarriageReturn(txt) {
var tmp = txt;
do {
txt = tmp;
tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
} while (tmp.length < txt.length);
return txt;
}
// Locate any URLs and convert them to a anchor tag
function autoLinkUrls(txt) {
return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
"$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
}
// some keycodes that seem to be platform/browser independent
var keycodes = {
BACKSPACE: 8,
TAB : 9,
ENTER : 13,
SHIFT : 16,
CTRL : 17,
CONTROL : 17,
ALT : 18,
CAPS_LOCK: 20,
ESC : 27,
SPACE : 32,
PGUP : 33,
PGDOWN : 34,
END : 35,
HOME : 36,
LEFT_ARROW: 37,
LEFTARROW: 37,
LEFT : 37,
UP_ARROW : 38,
UPARROW : 38,
UP : 38,
RIGHT_ARROW:39,
RIGHTARROW:39,
RIGHT : 39,
DOWN_ARROW: 40,
DOWNARROW: 40,
DOWN : 40,
I : 73,
M : 77,
// all three of these keys may be COMMAND on OS X:
LEFT_SUPER : 91,
RIGHT_SUPER : 92,
COMMAND : 93,
};
// trigger a key press event
var press = function (key) {
var key_press = $.Event('keydown', {which: key});
$(document).trigger(key_press);
}
var press_up = function() { press(keycodes.UP); };
var press_down = function() { press(keycodes.DOWN); };
var press_ctrl_enter = function() {
$(document).trigger($.Event('keydown', {which: keycodes.ENTER, ctrlKey: true}));
};
var press_shift_enter = function() {
$(document).trigger($.Event('keydown', {which: keycodes.ENTER, shiftKey: true}));
};
// trigger the ctrl-m shortcut followed by one of our keys
var press_ghetto = function(key) {
$(document).trigger($.Event('keydown', {which: keycodes.M, ctrlKey: true}));
press(key);
};
var points_to_pixels = function (points) {
// A reasonably good way of converting between points and pixels.
var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
$(body).append(test);
var pixel_per_point = test.width()/10000;
test.remove();
return Math.floor(points*pixel_per_point);
};
var always_new = function (constructor) {
// wrapper around contructor to avoid requiring `var a = new constructor()`
// useful for passing constructors as callbacks,
// not for programmer laziness.
// from http://programmers.stackexchange.com/questions/118798
return function () {
var obj = Object.create(constructor.prototype);
constructor.apply(obj, arguments);
return obj;
};
};
var url_path_join = function () {
// join a sequence of url components with '/'
var url = '';
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] === '') {
continue;
}
if (url.length > 0 && url[url.length-1] != '/') {
url = url + '/' + arguments[i];
} else {
url = url + arguments[i];
}
}
return url;
};
var encode_uri_components = function (uri) {
// encode just the components of a multi-segment uri,
// leaving '/' separators
return uri.split('/').map(encodeURIComponent).join('/');
}
var url_join_encode = function () {
// join a sequence of url components with '/',
// encoding each component with encodeURIComponent
return encode_uri_components(url_path_join.apply(null, arguments));
};
var splitext = function (filename) {
// mimic Python os.path.splitext
// Returns ['base', '.ext']
var idx = filename.lastIndexOf('.');
if (idx > 0) {
return [filename.slice(0, idx), filename.slice(idx)];
} else {
return [filename, ''];
}
}
// http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
var browser = (function() {
var N= navigator.appName, ua= navigator.userAgent, tem;
var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
return M;
})();
var is_or_has = function (a, b) {
// Is b a child of a or a itself?
return a.has(b).length !==0 || a.is(b);
}
var is_focused = function (e) {
// Is element e, or one of its children focused?
e = $(e);
var target = $(document.activeElement);
if (target.length > 0) {
if (is_or_has(e, target)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
return {
regex_split : regex_split,
uuid : uuid,
fixConsole : fixConsole,
keycodes : keycodes,
press : press,
press_up : press_up,
press_down : press_down,
press_ctrl_enter : press_ctrl_enter,
press_shift_enter : press_shift_enter,
press_ghetto : press_ghetto,
fixCarriageReturn : fixCarriageReturn,
autoLinkUrls : autoLinkUrls,
points_to_pixels : points_to_pixels,
url_path_join : url_path_join,
url_join_encode : url_join_encode,
encode_uri_components : encode_uri_components,
splitext : splitext,
always_new : always_new,
browser : browser,
is_or_has : is_or_has,
is_focused : is_focused
};
}(IPython));