diff --git a/demos/syntax/js/syntax.js b/demos/syntax/js/syntax.js
index 4a702d89..b9e98c4e 100644
--- a/demos/syntax/js/syntax.js
+++ b/demos/syntax/js/syntax.js
@@ -3,48 +3,61 @@ Ox.load('UI', {
theme: 'classic'
}, function() {
- Ox.Theme('classic');
+ var $button = Ox.Button({
+ title: 'Run',
+ width: 256
+ })
+ .css({
+ marginTop: '256px'
+ })
+ .click(function() {
+ $syntaxHighlighter.options({
+ source: $textarea.value()
+ });
+ }),
+ $options = Ox.Element()
+ .append(
+ Ox.FormElementGroup({
+ elements: ['showLineNumbers', 'showLinebreaks', 'showTabs', 'showWhitespace'].map(function(v, i) {
+ return Ox.Checkbox({
+ overlap: 'right',
+ title: Ox.toDashes(v).split('-').map(function(v) { return Ox.toTitleCase(v); }).join(' '),
+ width: 160
+ }).bindEvent({
+ change: function(event) {
+ $syntaxHighlighter.options(v, event.checked);
+ }
+ })
+ })
+ })
+ ),
+ $syntaxHighlighter = Ox.SyntaxHighlighter(),
+ $textarea = Ox.Input({
+ height: 256,
+ type: 'textarea',
+ width: 256
+ })
+ .css({
+ fontFamily: 'Menlo, Monaco, Courier, Courier New'
+ });
- var $body = $('body'),
- $textarea = new Ox.Input({
- height: 400,
- type: 'textarea',
- width: 400
- })
- .css({
- fontFamily: 'Menlo, Monaco, Courier, Courier New'
- })
- .appendTo($body),
- $button = new Ox.Button({
- title: 'Run',
- width: 40
- })
- .css({
- position: 'absolute',
- left: '8px',
- top: '416px',
- })
- .bindEvent({
- click: function() {
- $div.empty();
- new Ox.SyntaxHighlighter({
- showLinebreaks: true,
- showTabs: true,
- showWhitespace: true,
- source: $textarea.value(),
- //stripComments: true,
- //stripLinebreaks: true,
- //stripWhitespace: true,
- }).appendTo($div);
- }
- })
- .appendTo($body),
- $div = $('
')
- .css({
- position: 'absolute',
- left: '416px',
- top: '8px'
- })
- .appendTo($body);
+ Ox.SplitPanel({
+ elements: [
+ {
+ element: Ox.Element()
+ .append($textarea)
+ .append($button),
+ resizable: true,
+ resize: [128, 256, 384],
+ size: 256
+ },
+ {
+ element: Ox.Container()
+ .append($options)
+ .append($syntaxHighlighter)
+ }
+ ],
+ orientation: 'horizontal'
+ }).appendTo(Ox.UI.$body)
});
\ No newline at end of file
diff --git a/source/Ox.UI/js/Core/Ox.DocPage.js b/source/Ox.UI/js/Core/Ox.DocPage.js
index 5bd64f34..8730aa02 100644
--- a/source/Ox.UI/js/Core/Ox.DocPage.js
+++ b/source/Ox.UI/js/Core/Ox.DocPage.js
@@ -129,6 +129,7 @@ Ox.DocPage = function(options, self) {
);
$elements.push(
Ox.SyntaxHighlighter({
+ showLineNumbers: true,
// fixme: silly
source: item.source.map(function(token) {
return token.source;
diff --git a/source/Ox.UI/js/Core/Ox.SyntaxHighlighter.js b/source/Ox.UI/js/Core/Ox.SyntaxHighlighter.js
index 25ce2cf6..ce7b6152 100644
--- a/source/Ox.UI/js/Core/Ox.SyntaxHighlighter.js
+++ b/source/Ox.UI/js/Core/Ox.SyntaxHighlighter.js
@@ -7,11 +7,12 @@
Ox.SyntaxHighlighter = function(options, self) {
self = self || {};
- var that = new Ox.Element({}, self)
+ var that = Ox.Element({}, self)
.defaults({
height: 40,
lineLength: 80, //@ number of characters per line
offset: 1, //@ first line number
+ showLineNumbers: false,
showLinebreaks: false, //@ show linebreak symbols
showTabs: false, //@ show tab symbols
showWhitespace: false, //@ show irregular leading or trailing whitespace
@@ -25,71 +26,7 @@ Ox.SyntaxHighlighter = function(options, self) {
.options(options || {})
.addClass('OxSyntaxHighlighter');
- self.options.source = self.options.source
- .replace(/\r\n/g, '\n')
- .replace(/\r/g, '\n');
-
- self.cursor = 0;
- self.source = '';
- self.tokens = Ox.tokenize(self.options.source);
- self.tokens.forEach(function(token, i) {
- var classNames,
- source = self.options.source.substr(token.offset, token.length);
- if (
- !(self.options.stripComments && token.type == 'comment')
- ) {
- classNames = 'Ox' + Ox.toTitleCase(token.type);
- if (self.options.showWhitespace && token.type == 'whitespace') {
- if (isAfterLinebreak() && hasIrregularSpaces()) {
- classNames += ' OxLeading'
- } else if (isBeforeLinebreak()) {
- classNames += ' OxTrailing'
- }
- }
- self.source += '' +
- encodeToken(source, token.type) + '';
- }
- self.cursor += token.length;
- function isAfterLinebreak() {
- return i == 0 ||
- self.tokens[i - 1].type == 'linebreak';
- }
- function isBeforeLinebreak() {
- return i == self.tokens.length - 1 ||
- self.tokens[i + 1].type == 'linebreak';
- }
- function hasIrregularSpaces() {
- return source.split('').reduce(function(prev, curr) {
- return prev + (curr == ' ' ? 1 : 0);
- }, 0) % self.options.tabLength;
- }
- });
- self.lines = self.source.split('
');
- self.lineNumbersWidth = (
- self.lines.length + self.options.offset - 1
- ).toString().length * 7;
-
- self.$lineNumbers = new Ox.Element()
- .addClass('OxLineNumbers')
- .css({
- display: 'table-cell',
- width: self.lineNumbersWidth + 'px',
- padding: '4px',
- })
- .html(
- Ox.range(self.lines.length).map(function(line) {
- return (line + self.options.offset);
- }).join('
')
- )
- .appendTo(that);
- self.$source = new Ox.Element()
- .addClass('OxSourceCode')
- .css({
- display: 'table-cell',
- padding: '4px'
- })
- .html(self.source)
- .appendTo(that);
+ renderSource();
function encodeToken(source, token) {
var linebreak = '
',
@@ -110,8 +47,74 @@ Ox.SyntaxHighlighter = function(options, self) {
.replace(/\n/g, linebreak);
}
- self.setOption = function() {
-
+ function renderSource() {
+ self.options.source = self.options.source
+ .replace(/\r\n/g, '\n')
+ .replace(/\r/g, '\n');
+ self.cursor = 0;
+ self.source = '';
+ self.tokens = Ox.tokenize(self.options.source);
+ self.tokens.forEach(function(token, i) {
+ var classNames,
+ source = self.options.source.substr(token.offset, token.length);
+ if (
+ !(self.options.stripComments && token.type == 'comment')
+ ) {
+ classNames = 'Ox' + Ox.toTitleCase(token.type);
+ if (self.options.showWhitespace && token.type == 'whitespace') {
+ if (isAfterLinebreak() && hasIrregularSpaces()) {
+ classNames += ' OxLeading'
+ } else if (isBeforeLinebreak()) {
+ classNames += ' OxTrailing'
+ }
+ }
+ self.source += '' +
+ encodeToken(source, token.type) + '';
+ }
+ self.cursor += token.length;
+ function isAfterLinebreak() {
+ return i == 0 ||
+ self.tokens[i - 1].type == 'linebreak';
+ }
+ function isBeforeLinebreak() {
+ return i == self.tokens.length - 1 ||
+ self.tokens[i + 1].type == 'linebreak';
+ }
+ function hasIrregularSpaces() {
+ return source.split('').reduce(function(prev, curr) {
+ return prev + (curr == ' ' ? 1 : 0);
+ }, 0) % self.options.tabLength;
+ }
+ });
+ self.lines = self.source.split('
');
+
+ that.empty();
+ if (self.options.showLineNumbers) {
+ self.$lineNumbers = new Ox.Element()
+ .addClass('OxLineNumbers')
+ .css({
+ display: 'table-cell',
+ padding: '4px',
+ })
+ .html(
+ Ox.range(self.lines.length).map(function(line) {
+ return (line + self.options.offset);
+ }).join('
')
+ )
+ .appendTo(that);
+ }
+ self.$source = new Ox.Element()
+ .addClass('OxSourceCode')
+ .css({
+ display: 'table-cell',
+ padding: '4px'
+ })
+ .html(self.source)
+ .appendTo(that);
+ }
+
+ self.setOption = function(key, value) {
+ renderSource();
};
return that;
diff --git a/source/Ox.js b/source/Ox.js
index cbc106ce..3b408792 100644
--- a/source/Ox.js
+++ b/source/Ox.js
@@ -73,7 +73,7 @@ Ox.print Prints its arguments to the console
The string contains the timestamp, the name of the caller function, and
any arguments, separated by spaces
arg <*> any value
- > Ox.print("foo").substr(-3)
+ > Ox.print('foo').split(' ').pop()
"foo"
@*/
@@ -289,7 +289,26 @@ Ox.clone = function(obj) {
};
/*@
-Ox.count Counts the occurences of values in an array, object or string
+Ox.contains Tests if a collection contains a value
+ > Ox.contains(['foo', 'bar'], 'foo')
+ true
+ > Ox.contains({foo: 'bar'}, 'bar')
+ true
+ > Ox.contains({foo: 'bar'}, 'foo')
+ false
+ > Ox.contains("foobar", "bar")
+ true
+@*/
+Ox.contains = function(col, val) {
+ /*
+ // fixme: rename to Ox.has or Ox.isIn?
+ // then it'd become convenient for arrays
+ */
+ return (Ox.isObject(col) ? Ox.values(col) : col).indexOf(val) > -1;
+};
+
+/*@
+Ox.count Counts the occurences of values in a collection
> Ox.count(['f', 'o', 'o'])
{f: 1, o: 2}
> Ox.count({a: 'f', b: 'o', c: 'o'})
@@ -330,7 +349,7 @@ Ox.each = function(obj, fn) {
};
/*@
-Ox.every Returns true if a condition holds for every element of a collection
+Ox.every Tests if every element of a collection satisfies a given condition
Unlike [].every()
, Ox.every()
works for arrays,
objects and strings.
> Ox.every([0, 1, 2], function(v, i) { return i == v; })
@@ -436,13 +455,11 @@ Ox.forEach = function(obj, fn) {
/*@
Ox.getObjectById Returns an array element with a given id
+ > Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
+ {id: "foo", title: "Foo"}
@*/
// fixme: shouldn't this be getElementById() ?
Ox.getObjectById = function(arr, id) {
- /***
- >>> Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo").title
- "Foo"
- ***/
var ret = null;
Ox.forEach(arr, function(v) {
if (v.id == id) {
@@ -455,13 +472,11 @@ Ox.getObjectById = function(arr, id) {
/*@
Ox.getPositionById Returns the index of an array element with a given id
+ > Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
+ 0
@*/
// fixme: shouldn't this be getIndexById() ?
Ox.getPositionById = function(arr, id) {
- /***
- >>> Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "bar")
- 1
- ***/
var ret = -1;
Ox.forEach(arr, function(v, i) {
if (v.id == id) {
@@ -543,6 +558,8 @@ Ox.isEmpty Returns true if a collection is empty
true
> Ox.isEmpty('')
true
+ > Ox.isEmpty(function() {})
+ true
@*/
Ox.isEmpty = function(val) {
return Ox.len(val) == 0;
@@ -620,7 +637,7 @@ Ox.len = function(obj) {
/*@
Ox.loop For-loop, functional-style
- Returning false
from the iterater function acts like a
+ Returning false
from the iterator function acts like a
break
statement. Unlike a for
loop,
Ox.loop
doesn't leak its counter variable to the outer scope,
but returns it.
@@ -630,8 +647,10 @@ Ox.loop For-loop, functional-style
equivalent to for (var i = start; i < stop; i++)
or,
if start
is larger than stop
,
for (var i = start; i > stop; i--)
- (start, stop, step) -> Next value
- equivalent to for (var i = start; i < stop; i += step)
+ (start, stop, step, callback) -> Next value
+ equivalent to for (var i = start; i < stop; i += step)
or,
+ if step
is negative,
+ for (var i = start; i > stop; i += step)
start Start value
stop Stop value (exclusive)
step Step value
@@ -919,8 +938,7 @@ Ox.values Returns the values of a collection
[1, 3]
@*/
Ox.values = function(col) {
- // this happens to works for arrays and strings, but still:
- // Ox.values(arr) -> arr, Ox.values(str) -> str.split('')
+ // Ox.values(str) is identical to str.split('')
var values = [];
Ox.forEach(col, function(val) {
values.push(val);
@@ -3614,7 +3632,9 @@ Ox.sinh = function(x) {
//@ Constants ------------------------------------------------------------------
+//@ Ox.MAX_LATITUDE Maximum latitude of a Mercator projection
Ox.MAX_LATITUDE = Ox.deg(Math.atan(Ox.sinh(Math.PI)));
+//@ Ox.MIN_LATITUDE Minimum latitude of a Mercator projection
Ox.MIN_LATITUDE = -Ox.MAX_LATITUDE;
//@ Object ---------------------------------------------------------------------
@@ -3811,44 +3831,32 @@ Ox.char Alias for String.fromCharCode
// fixme: add some mapping? like Ox.char(9, 13) or Ox.char([9, 13])?
Ox.char = String.fromCharCode;
-Ox.clean = function(str) {
- /*
- >>> Ox.clean("foo bar")
+/*@
+Ox.clean Remove leading, trailing and double whitespace from a string
+ > Ox.clean("foo bar")
"foo bar"
- >>> Ox.clean(" foo bar ")
+ > Ox.clean(" foo bar ")
"foo bar"
- >>> Ox.clean(" foo \n bar ")
+ > Ox.clean(" foo \n bar ")
"foo\nbar"
- */
+@*/
+Ox.clean = function(str) {
return Ox.map(str.split('\n'), function(str) {
return Ox.trim(str.replace(/\s+/g, ' '));
}).join('\n');
};
-Ox.contains = function(str, chr) {
- /*
- >>> Ox.contains("foo", "bar")
- false
- >>> Ox.contains("foobar", "bar")
- true
- >>> Ox.contains(['foo', 'bar'], 'foo')
- true
- // fixme: rename to Ox.has or Ox.isIn?
- // then it'd become convenient for arrays
- */
- return str.indexOf(chr) > -1;
-};
-
/*@
Ox.endsWith Checks if a string ends with a given substring
- While Ox.endsWith('foobar', 'bar')
is longer than
- /bar$/.test('foobar')
, Ox.endsWith('foobar', bar)
- is shorter than new RegExp(bar + '$').test('foobar')
.
+ If the substring is a string literal (and not a variable),
+ /sub$/.test(str)
or !!/sub$/(str)
+ is shorter than Ox.ends(str, sub)
.
> Ox.endsWith('foobar', 'bar')
true
@*/
Ox.endsWith = function(str, sub) {
- return new RegExp(sub + '$').test(str);
+ // fixme: rename to ends
+ return str.substr(str.length - sub.length) == sub;
};
Ox.highlight = function(txt, str) {
@@ -3928,23 +3936,27 @@ Ox.parsePath = function(str) {
};
}
-Ox.repeat = function(obj, num) {
- /*
- works for arrays, numbers and strings
- >>> Ox.repeat(1, 3)
+/*@
+Ox.repeat Repeat a value multiple times
+ Works for arrays, numbers and strings
+ > Ox.repeat(1, 3)
"111"
- >>> Ox.repeat("foo", 3)
+ > Ox.repeat("foo", 3)
"foofoofoo"
- >>> Ox.repeat([1, 2], 3)
+ > Ox.repeat([1, 2], 3)
[1, 2, 1, 2, 1, 2]
- */
+@*/
+Ox.repeat = function(val, num) {
var ret;
- if (Ox.isArray(obj)) {
- ret = num >= 1 ? Ox.map(Ox.range(obj.length * num), function(v, i) {
- return obj[i % obj.length]
- }) : [];
+ if (Ox.isArray(val)) {
+ ret = [];
+ if (num >= 1) {
+ Ox.loop(num, function() {
+ ret = Ox.merge(ret, val);
+ });
+ }
} else {
- ret = num >= 1 ? new Array(num + 1).join(obj.toString()) : '';
+ ret = num >= 1 ? new Array(num + 1).join(val.toString()) : '';
}
return ret;
};
@@ -3959,22 +3971,15 @@ Ox.reverse = function(str) {
/*@
Ox.startsWith Checks if a string starts with a given substring
- While Ox.startsWith('foobar', 'foo')
is longer than
- /^foo/.test('foobar')
, Ox.startsWith('foobar', foo)
- is shorter than new RegExp('^' + foo).test('foobar')
.
- > Ox.endsWith('foobar', 'bar')
+ If the substring is a string literal (and not a variable),
+ /^sub/.test(str)
or !!/^sub/(str)
+ is shorter than Ox.starts(str, sub)
.
+ > Ox.startsWith('foobar', 'foo')
true
@*/
Ox.startsWith = function(str, sub) {
- /*
- >>> Ox.startsWith("foobar", "foo")
- true
- // fixme:
- // !!(/^sub/(str)) is shorter than
- // Ox.startsWith(str, sub) anyway
- // new RegExp('^' + sub).test(str) is longer though...
- */
- return new RegExp('^' + sub).test(str);
+ // fixme: rename to starts
+ return str.substr(0, sub.length) == sub;
};
/*@
@@ -3983,26 +3988,29 @@ Ox.stripTags Strips HTML tags from a string
'foo'
@*/
Ox.stripTags = function(str) {
- return str.replace(/(<.*?>)/g, '');
+ return str.replace(/<.*?>/g, '');
};
+/*@
+Ox.substr A better substr
+ > Ox.substr('foobar', 1)
+ "oobar"
+ > Ox.substr('foobar', -1)
+ "r"
+ > Ox.substr('foobar', 1, 5)
+ "ooba"
+ > Ox.substr('foobar', 1, -1)
+ "ooba"
+ > Ox.substr('foobar', -5, 5)
+ "ooba"
+ > Ox.substr('foobar', -5, -1)
+ "ooba"
+@*/
Ox.substr = function(str, start, stop) {
/***
+ // fixme: needed?
Ox.substr behaves like str[start:stop] in Python
(or like str.substring() with negative values for stop)
- // fixme: needed?
- >>> Ox.substr('foobar', 1)
- "oobar"
- >>> Ox.substr('foobar', -1)
- "r"
- >>> Ox.substr('foobar', 1, 3)
- "oo"
- >>> Ox.substr('foobar', -3, 5)
- "ba"
- >>> Ox.substr('foobar', 1, -2)
- "oob"
- >>> Ox.substr('foobar', -4, -1)
- "oba"
***/
stop = Ox.isUndefined(stop) ? str.length : stop;
return str.substring(