Skip to content

Commit 67e04cf

Browse files
author
Dmitriy Kubyshkin
committed
Created DOM structure for the editor.
1 parent 5398dd7 commit 67e04cf

File tree

11 files changed

+3020
-14
lines changed

11 files changed

+3020
-14
lines changed

Makefile

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,5 @@ build:
66
serve:
77
node ./scripts/serve.js
88

9-
# Runs Mocha test suite
10-
test:
11-
@./node_modules/.bin/mocha -r should -r sinon
12-
139
# These aren't real targets so we need to list them here
14-
.PHONY: test build serve
10+
.PHONY: build serve

demo/index.html

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
<meta name="keywords" content="">
99
<script type="text/javascript" src="http://localhost:4000/lib.js"></script>
1010
<script type="text/javascript" charset="utf-8">
11-
var CanvasTextEditor = require('editor');
11+
document.addEventListener('DOMContentLoaded', function(){
12+
var CanvasTextEditor = require('editor');
13+
var editor = new CanvasTextEditor;
14+
document.body.appendChild(editor.getEl());
15+
}, false);
1216
</script>
1317
</head>
1418
<body>

lib/editor.js

+102-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,104 @@
1-
var CanvasTextEditor = function() {};
1+
/**
2+
* Simple plain-text text editor using html5 canvas.
3+
* @constructor
4+
*/
5+
var CanvasTextEditor = function() {
6+
this._createWrapper();
7+
this._createCanvas();
8+
this._createInput();
9+
};
10+
211
module.exports = CanvasTextEditor;
312

13+
/**
14+
* CSS class that is assigned to the wrapper.
15+
* @type {String}
16+
*/
17+
CanvasTextEditor.prototype.className = 'canvas-text-editor';
18+
19+
/**
20+
* Creates wrapper element for all parts of the editor
21+
* @private
22+
*/
23+
CanvasTextEditor.prototype._createWrapper = function() {
24+
this.wrapper = document.createElement('div');
25+
this.wrapper.className = this.className;
26+
this.wrapper.style.display = 'inline-block';
27+
this.wrapper.style.backgroundColor = '#eee';
28+
this.wrapper.style.overflow = 'hidden';
29+
this.wrapper.tabIndex = 0; // tabindex is necessary to get focus
30+
this.wrapper.addEventListener('focus', this.focus.bind(this), false);
31+
};
32+
33+
/**
34+
* Creates canvas for drawing
35+
* @private
36+
*/
37+
CanvasTextEditor.prototype._createCanvas = function() {
38+
this.canvas = document.createElement('canvas');
39+
this.wrapper.appendChild(this.canvas);
40+
this.canvas.style.display = 'block';
41+
this.context = this.canvas.getContext('2d');
42+
this.resize(640, 480);
43+
44+
// Placeholder function just to see that it's working
45+
this.context.fillText('Test', 0, 10);
46+
};
47+
48+
/**
49+
* Creates textarea that will handle user input and copy-paste actions
50+
* @private
51+
*/
52+
CanvasTextEditor.prototype._createInput = function() {
53+
this.inputEl = document.createElement('textarea');
54+
this.inputEl.style.position = 'absolute';
55+
this.inputEl.style.top = '-100px';
56+
this.inputEl.style.height = 0;
57+
this.inputEl.style.width = 0;
58+
this.inputEl.addEventListener('blur', this.blur.bind(this), false);
59+
this.inputEl.addEventListener('focus', this._inputFocus.bind(this), false);
60+
this.inputEl.tabIndex = -1; // we don't want input to get focus by tabbing
61+
this.wrapper.appendChild(this.inputEl);
62+
};
63+
64+
/**
65+
* Real handler code for editor gaining focus.
66+
* @private
67+
*/
68+
CanvasTextEditor.prototype._inputFocus = function() {
69+
this.wrapper.style.outline = '1px solid #09f';
70+
};
71+
72+
/**
73+
* Returns main editor node so it can be inserted into document.
74+
* @return {HTMLElement}
75+
*/
76+
CanvasTextEditor.prototype.getEl = function() {
77+
return this.wrapper;
78+
};
79+
80+
/**
81+
* Resizes editor to provided dimensions.
82+
* @param {Number} width
83+
* @param {Number} height
84+
*/
85+
CanvasTextEditor.prototype.resize = function(width, height) {
86+
this.canvas.width = width;
87+
this.canvas.height = height;
88+
};
89+
90+
/**
91+
* Blur handler.
92+
*/
93+
CanvasTextEditor.prototype.blur = function() {
94+
this.inputEl.blur();
95+
this.wrapper.style.outline = 'none';
96+
};
97+
98+
/**
99+
* Focus handler. Acts as a proxy to input focus.
100+
*/
101+
CanvasTextEditor.prototype.focus = function() {
102+
this.inputEl.focus();
103+
};
104+

package.json

+1-7
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,10 @@
66
"engines": {
77
"node": ">= 0.4.x < 0.7.0"
88
},
9-
"scripts": {
10-
"test": "make test"
11-
},
129
"devDependencies": {
1310
"coffee-script": "latest",
1411
"stitch": "latest",
1512
"uglify-js": "latest",
16-
"express": "latest",
17-
"mocha": "latest",
18-
"should": "latest",
19-
"sinon": "latest"
13+
"express": "latest"
2014
}
2115
}

test/lib/jasmine-1.1.0/MIT.LICENSE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2008-2011 Pivotal Labs
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
jasmine.TrivialReporter = function(doc) {
2+
this.document = doc || document;
3+
this.suiteDivs = {};
4+
this.logRunningSpecs = false;
5+
};
6+
7+
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
8+
var el = document.createElement(type);
9+
10+
for (var i = 2; i < arguments.length; i++) {
11+
var child = arguments[i];
12+
13+
if (typeof child === 'string') {
14+
el.appendChild(document.createTextNode(child));
15+
} else {
16+
if (child) { el.appendChild(child); }
17+
}
18+
}
19+
20+
for (var attr in attrs) {
21+
if (attr == "className") {
22+
el[attr] = attrs[attr];
23+
} else {
24+
el.setAttribute(attr, attrs[attr]);
25+
}
26+
}
27+
28+
return el;
29+
};
30+
31+
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
32+
var showPassed, showSkipped;
33+
34+
this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
35+
this.createDom('div', { className: 'banner' },
36+
this.createDom('div', { className: 'logo' },
37+
this.createDom('span', { className: 'title' }, "Jasmine"),
38+
this.createDom('span', { className: 'version' }, runner.env.versionString())),
39+
this.createDom('div', { className: 'options' },
40+
"Show ",
41+
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
42+
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
43+
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
44+
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
45+
)
46+
),
47+
48+
this.runnerDiv = this.createDom('div', { className: 'runner running' },
49+
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
50+
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
51+
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
52+
);
53+
54+
this.document.body.appendChild(this.outerDiv);
55+
56+
var suites = runner.suites();
57+
for (var i = 0; i < suites.length; i++) {
58+
var suite = suites[i];
59+
var suiteDiv = this.createDom('div', { className: 'suite' },
60+
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
61+
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
62+
this.suiteDivs[suite.id] = suiteDiv;
63+
var parentDiv = this.outerDiv;
64+
if (suite.parentSuite) {
65+
parentDiv = this.suiteDivs[suite.parentSuite.id];
66+
}
67+
parentDiv.appendChild(suiteDiv);
68+
}
69+
70+
this.startedAt = new Date();
71+
72+
var self = this;
73+
showPassed.onclick = function(evt) {
74+
if (showPassed.checked) {
75+
self.outerDiv.className += ' show-passed';
76+
} else {
77+
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
78+
}
79+
};
80+
81+
showSkipped.onclick = function(evt) {
82+
if (showSkipped.checked) {
83+
self.outerDiv.className += ' show-skipped';
84+
} else {
85+
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
86+
}
87+
};
88+
};
89+
90+
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
91+
var results = runner.results();
92+
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
93+
this.runnerDiv.setAttribute("class", className);
94+
//do it twice for IE
95+
this.runnerDiv.setAttribute("className", className);
96+
var specs = runner.specs();
97+
var specCount = 0;
98+
for (var i = 0; i < specs.length; i++) {
99+
if (this.specFilter(specs[i])) {
100+
specCount++;
101+
}
102+
}
103+
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
104+
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
105+
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
106+
107+
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
108+
};
109+
110+
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
111+
var results = suite.results();
112+
var status = results.passed() ? 'passed' : 'failed';
113+
if (results.totalCount === 0) { // todo: change this to check results.skipped
114+
status = 'skipped';
115+
}
116+
this.suiteDivs[suite.id].className += " " + status;
117+
};
118+
119+
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
120+
if (this.logRunningSpecs) {
121+
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
122+
}
123+
};
124+
125+
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
126+
var results = spec.results();
127+
var status = results.passed() ? 'passed' : 'failed';
128+
if (results.skipped) {
129+
status = 'skipped';
130+
}
131+
var specDiv = this.createDom('div', { className: 'spec ' + status },
132+
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
133+
this.createDom('a', {
134+
className: 'description',
135+
href: '?spec=' + encodeURIComponent(spec.getFullName()),
136+
title: spec.getFullName()
137+
}, spec.description));
138+
139+
140+
var resultItems = results.getItems();
141+
var messagesDiv = this.createDom('div', { className: 'messages' });
142+
for (var i = 0; i < resultItems.length; i++) {
143+
var result = resultItems[i];
144+
145+
if (result.type == 'log') {
146+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
147+
} else if (result.type == 'expect' && result.passed && !result.passed()) {
148+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
149+
150+
if (result.trace.stack) {
151+
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
152+
}
153+
}
154+
}
155+
156+
if (messagesDiv.childNodes.length > 0) {
157+
specDiv.appendChild(messagesDiv);
158+
}
159+
160+
this.suiteDivs[spec.suite.id].appendChild(specDiv);
161+
};
162+
163+
jasmine.TrivialReporter.prototype.log = function() {
164+
var console = jasmine.getGlobal().console;
165+
if (console && console.log) {
166+
if (console.log.apply) {
167+
console.log.apply(console, arguments);
168+
} else {
169+
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
170+
}
171+
}
172+
};
173+
174+
jasmine.TrivialReporter.prototype.getLocation = function() {
175+
return this.document.location;
176+
};
177+
178+
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
179+
var paramMap = {};
180+
var params = this.getLocation().search.substring(1).split('&');
181+
for (var i = 0; i < params.length; i++) {
182+
var p = params[i].split('=');
183+
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
184+
}
185+
186+
if (!paramMap.spec) {
187+
return true;
188+
}
189+
return spec.getFullName().indexOf(paramMap.spec) === 0;
190+
};

0 commit comments

Comments
 (0)