sottolio 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +676 -0
- data/README.md +26 -0
- data/Rakefile +12 -0
- data/bin/sottolio +29 -0
- data/example/game/include/CanvasText-0.4.js +500 -0
- data/example/game/include/canvasinput.js +379 -0
- data/example/game/include/howler.min.js +10 -0
- data/example/game/include/jquery.min.js +4 -0
- data/example/game/index.html +19 -0
- data/example/game/resources/backgrounds/city.jpg +0 -0
- data/example/game/resources/characters/ambrogia.png +0 -0
- data/example/game/resources/characters/rosalinda.png +0 -0
- data/example/game/resources/right_arrow.png +0 -0
- data/example/game/resources/sounds/Classmate.m4a +0 -0
- data/example/scripts/chapter_1.rb +64 -0
- data/example/scripts/chapter_2.rb +23 -0
- data/example/scripts/script.rb +2 -0
- data/lib/sottolio.rb +19 -0
- data/lib/sottolio/sottolio.rb +22 -0
- data/lib/sottolio/version.rb +21 -0
- data/opal/sottolio.rb +38 -0
- data/opal/sottolio/application.rb +134 -0
- data/opal/sottolio/database.rb +48 -0
- data/opal/sottolio/image_manager.rb +46 -0
- data/opal/sottolio/lock.rb +41 -0
- data/opal/sottolio/script.rb +93 -0
- data/opal/sottolio/sottolio.rb +29 -0
- data/opal/sottolio/sound_manager.rb +55 -0
- data/opal/sottolio/utils.rb +57 -0
- data/opal/sottolio/wrapper/background.rb +21 -0
- data/opal/sottolio/wrapper/canvas.rb +90 -0
- data/opal/sottolio/wrapper/canvas/canvas_button.rb +69 -0
- data/opal/sottolio/wrapper/canvas/canvas_input.rb +73 -0
- data/opal/sottolio/wrapper/canvas/canvas_text.rb +64 -0
- data/opal/sottolio/wrapper/character.rb +21 -0
- data/opal/sottolio/wrapper/image.rb +65 -0
- data/opal/sottolio/wrapper/sound.rb +55 -0
- data/sottolio.gemspec +20 -0
- metadata +114 -0
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Sottolio - È sopraffino
|
2
|
+
========================
|
3
|
+
Porting of [Sottaceto](https://github.com/RoxasShadow/Sottaceto) in JavaScript written in Ruby thanks to Opal.
|
4
|
+
|
5
|
+
Just like Sottaceto, sottolio is a game engine to create visual novels with ease. These games run everywhere, you only need a~~~n internet browser which supports JavaScript and HTML5~~~ decent internet browser.
|
6
|
+
|
7
|
+
The scripts (check `compiler/scripts`) are pretty self-explanatory (even more than Sottaceto's ones, actually).
|
8
|
+
|
9
|
+
Backgrounds, musics, and other stuff are kept inside `game/resources/`.
|
10
|
+
|
11
|
+
Setup
|
12
|
+
=====
|
13
|
+
`$ gem install sottolio`
|
14
|
+
|
15
|
+
`$ npm install -g uglify-js`
|
16
|
+
|
17
|
+
Run the demo
|
18
|
+
============
|
19
|
+
`$ sottolio example/scripts example/game/sottolio`
|
20
|
+
|
21
|
+
|
22
|
+
sottolio will generate `sottolio.js` and `sottolio.min.js` inside `example/game` that are nothing but the compiled version of the scripts inside `example/scripts`.
|
23
|
+
|
24
|
+
You're now ready to open `example/game/index.html` in your browser!
|
25
|
+
|
26
|
+
The [demo](http://www.giovannicapuano.net/sottolio/) is also available in the web, as well the [video gameplay](http://www.youtube.com/watch?v=djV_Z5OeBmg&feature=youtu.be) (it's a bit old tho).
|
data/Rakefile
ADDED
data/bin/sottolio
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright(C) 2013-2015 Giovanni Capuano <webmaster@giovannicapuano.net>
|
4
|
+
#
|
5
|
+
# This file is part of sottolio.
|
6
|
+
#
|
7
|
+
# sottolio is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# sottolio is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with sottolio. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
require 'sottolio'
|
21
|
+
|
22
|
+
abort 'Usage: sottolio <scripts> <output>' if ARGV.length != 2
|
23
|
+
|
24
|
+
Opal.append_path ARGV[0]
|
25
|
+
|
26
|
+
build = Opal::Builder.build('sottolio').to_s
|
27
|
+
|
28
|
+
File.write "#{ARGV[1]}.js", build
|
29
|
+
File.write "#{ARGV[1]}.min.js", Opal::Util.uglify(build)
|
@@ -0,0 +1,500 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2011 Pere Monfort Pàmies (http://www.pmphp.net)
|
3
|
+
* Official site: http://www.canvastext.com
|
4
|
+
*
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
6
|
+
* copy of this software and associated documentation files (the
|
7
|
+
* "Software"), to deal in the Software without restriction, including
|
8
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
10
|
+
* persons to whom the Software is furnished to do so, subject to the
|
11
|
+
* following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included
|
14
|
+
* in all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
17
|
+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
19
|
+
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
20
|
+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
21
|
+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
22
|
+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
*/
|
24
|
+
function CanvasText() {
|
25
|
+
// The property that will contain the ID attribute value.
|
26
|
+
this.canvasId = null;
|
27
|
+
// The property that will contain the Canvas element.
|
28
|
+
this.canvas = null;
|
29
|
+
// The property that will contain the canvas context.
|
30
|
+
this.context = null;
|
31
|
+
// The property that will contain the buffer/cache canvas.
|
32
|
+
this.bufferCanvas = null;
|
33
|
+
// The property that will contain the cacheCanvas context.
|
34
|
+
this.bufferContext = null;
|
35
|
+
// The property that will contain all cached canvas.
|
36
|
+
this.cacheCanvas = [];
|
37
|
+
// The property that will contain all cached contexts.
|
38
|
+
this.cacheContext = [];
|
39
|
+
// The property that will contain the created style class.
|
40
|
+
this.savedClasses = [];
|
41
|
+
|
42
|
+
/*
|
43
|
+
* Default values.
|
44
|
+
*/
|
45
|
+
this.fontFamily = "Verdana";
|
46
|
+
this.fontWeight = "normal";
|
47
|
+
this.fontSize = "12px";
|
48
|
+
this.fontColor = "#000";
|
49
|
+
this.fontStyle = "normal";
|
50
|
+
this.textAlign = "start";
|
51
|
+
this.textBaseline = "alphabetic";
|
52
|
+
this.lineHeight = "16";
|
53
|
+
this.textShadow = null;
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Benckmark variables.
|
57
|
+
*/
|
58
|
+
this.initTime = null;
|
59
|
+
this.endTime = null;
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Set the main values.
|
63
|
+
* @param object config Text properties.
|
64
|
+
*/
|
65
|
+
this.config = function (config) {
|
66
|
+
var property;
|
67
|
+
/*
|
68
|
+
* A simple check. If config is not an object popup an alert.
|
69
|
+
*/
|
70
|
+
if (typeof (config) !== "object") {
|
71
|
+
alert("¡Invalid configuration!");
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
/*
|
75
|
+
* Loop the config properties.
|
76
|
+
*/
|
77
|
+
for (property in config) {
|
78
|
+
// If it's a valid property, save it.
|
79
|
+
if (this[property] !== undefined) {
|
80
|
+
this[property] = config[property];
|
81
|
+
}
|
82
|
+
}
|
83
|
+
};
|
84
|
+
|
85
|
+
/**
|
86
|
+
* @param object textInfo Contains the Text string, axis X value and axis Y value.
|
87
|
+
*/
|
88
|
+
this.drawText = function (textInfo) {
|
89
|
+
this.initTime = new Date().getTime();
|
90
|
+
/*
|
91
|
+
* If this.canvas doesn't exist we will try to set it.
|
92
|
+
* This will be done the first execution time.
|
93
|
+
*/
|
94
|
+
if (this.canvas == null) {
|
95
|
+
if (!this.getCanvas()) {
|
96
|
+
alert("Incorrect canvas ID!");
|
97
|
+
return false;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
/**
|
101
|
+
*
|
102
|
+
*/
|
103
|
+
if (this.bufferCanvas == null) {
|
104
|
+
this.getBufferCanvas();
|
105
|
+
}
|
106
|
+
/**
|
107
|
+
* Get or set the cache if a cacheId exist.
|
108
|
+
*/
|
109
|
+
if (textInfo.cacheId !== undefined) {
|
110
|
+
// We add a prefix to avoid name conflicts.
|
111
|
+
textInfo.cacheId = "ct" + textInfo.cacheId;
|
112
|
+
// If cache exists: draw text and stop script execution.
|
113
|
+
if (this.getCache(textInfo.cacheId)) {
|
114
|
+
if (!textInfo.returnImage) {
|
115
|
+
this.context.drawImage(this.cacheCanvas[textInfo.cacheId], 0, 0);
|
116
|
+
} else if (textInfo.returnImage) {
|
117
|
+
return this.cacheCanvas[textInfo.cacheId];
|
118
|
+
}
|
119
|
+
|
120
|
+
this.endTime = new Date().getTime();
|
121
|
+
//console.log("cache",(this.endTime-this.initTime)/1000);
|
122
|
+
return false;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
// A simple check.
|
126
|
+
if (typeof (textInfo) != "object") {
|
127
|
+
alert("Invalid text format!");
|
128
|
+
return false;
|
129
|
+
}
|
130
|
+
// Another simple check
|
131
|
+
if (!this.isNumber(textInfo.x) || !this.isNumber(textInfo.y)) {
|
132
|
+
alert("You should specify a correct \"x\" & \"y\" axis value.");
|
133
|
+
return false;
|
134
|
+
}
|
135
|
+
// Reset our cacheCanvas.
|
136
|
+
this.bufferCanvas.width = this.bufferCanvas.width;
|
137
|
+
// Set the color.
|
138
|
+
this.bufferContext.fillStyle = this.fontColor;
|
139
|
+
// Set the size & font family.
|
140
|
+
this.bufferContext.font = this.fontWeight + ' ' + this.fontSize + ' ' + this.fontFamily;
|
141
|
+
// Parse and draw the styled text.
|
142
|
+
this.drawStyledText(textInfo);
|
143
|
+
// Cache the result.
|
144
|
+
if (textInfo.cacheId != undefined) {
|
145
|
+
this.setCache(textInfo.cacheId);
|
146
|
+
}
|
147
|
+
|
148
|
+
this.endTime = new Date().getTime();
|
149
|
+
//console.log((this.endTime-this.initTime)/1000);
|
150
|
+
// Draw or return the final image.
|
151
|
+
if (!textInfo.returnImage) {
|
152
|
+
this.context.drawImage(this.bufferCanvas, 0, 0);
|
153
|
+
} else if (textInfo.returnImage) {
|
154
|
+
return this.bufferCanvas;
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
/**
|
159
|
+
* The "painter". This will draw the styled text.
|
160
|
+
*/
|
161
|
+
this.drawStyledText = function (textInfo) {
|
162
|
+
// Save the textInfo into separated vars to work more comfortably.
|
163
|
+
var text = textInfo.text, x = textInfo.x, y = textInfo.y;
|
164
|
+
// Needed vars for automatic line break;
|
165
|
+
var splittedText, xAux, textLines = [], boxWidth = textInfo.boxWidth;
|
166
|
+
// Declaration of needed vars.
|
167
|
+
var proFont = [], properties, property, propertyName, propertyValue, atribute;
|
168
|
+
var classDefinition, proColor, proText, proShadow;
|
169
|
+
// Loop vars
|
170
|
+
var i, j, k, n;
|
171
|
+
|
172
|
+
// The main regex. Looks for <style>, <class> or <br /> tags.
|
173
|
+
var match = text.match(/<\s*br\s*\/>|<\s*class=["|']([^"|']+)["|']\s*\>([^>]+)<\s*\/class\s*\>|<\s*style=["|']([^"|']+)["|']\s*\>([^>]+)<\s*\/style\s*\>|[^<]+/g);
|
174
|
+
var innerMatch = null;
|
175
|
+
|
176
|
+
// Let's draw something for each match found.
|
177
|
+
for (i = 0; i < match.length; i++) {
|
178
|
+
// Save the current context.
|
179
|
+
this.bufferContext.save();
|
180
|
+
// Default color
|
181
|
+
proColor = this.fontColor;
|
182
|
+
// Default font
|
183
|
+
proFont.style = this.fontStyle;
|
184
|
+
proFont.weight = this.fontWeight;
|
185
|
+
proFont.size = this.fontSize;
|
186
|
+
proFont.family = this.fontFamily;
|
187
|
+
|
188
|
+
// Default shadow
|
189
|
+
proShadow = this.textShadow;
|
190
|
+
|
191
|
+
// Check if current fragment is an style tag.
|
192
|
+
if (/<\s*style=/i.test(match[i])) {
|
193
|
+
// Looks the attributes and text inside the style tag.
|
194
|
+
innerMatch = match[i].match(/<\s*style=["|']([^"|']+)["|']\s*\>([^>]+)<\s*\/style\s*\>/);
|
195
|
+
|
196
|
+
// innerMatch[1] contains the properties of the attribute.
|
197
|
+
properties = innerMatch[1].split(";");
|
198
|
+
|
199
|
+
// Apply styles for each property.
|
200
|
+
for (j = 0; j < properties.length; j++) {
|
201
|
+
// Each property have a value. We split them.
|
202
|
+
property = properties[j].split(":");
|
203
|
+
// A simple check.
|
204
|
+
if (this.isEmpty(property[0]) || this.isEmpty(property[1])) {
|
205
|
+
// Wrong property name or value. We jump to the
|
206
|
+
// next loop.
|
207
|
+
continue;
|
208
|
+
}
|
209
|
+
// Again, save it into friendly-named variables to work comfortably.
|
210
|
+
propertyName = property[0];
|
211
|
+
propertyValue = property[1];
|
212
|
+
|
213
|
+
switch (propertyName) {
|
214
|
+
case "font":
|
215
|
+
proFont = propertyValue;
|
216
|
+
break;
|
217
|
+
case "font-family":
|
218
|
+
proFont.family = propertyValue;
|
219
|
+
break;
|
220
|
+
case "font-weight":
|
221
|
+
proFont.weight = propertyValue;
|
222
|
+
break;
|
223
|
+
case "font-size":
|
224
|
+
proFont.size = propertyValue;
|
225
|
+
break;
|
226
|
+
case "font-style":
|
227
|
+
proFont.style = propertyValue;
|
228
|
+
break;
|
229
|
+
case "text-shadow":
|
230
|
+
proShadow = this.trim(propertyValue);
|
231
|
+
proShadow = proShadow.split(" ");
|
232
|
+
if (proShadow.length != 4) {
|
233
|
+
proShadow = null;
|
234
|
+
}
|
235
|
+
break;
|
236
|
+
case "color":
|
237
|
+
if (this.isHex(propertyValue)) {
|
238
|
+
proColor = propertyValue;
|
239
|
+
}
|
240
|
+
break;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
proText = innerMatch[2];
|
244
|
+
|
245
|
+
} else if (/<\s*class=/i.test(match[i])) { // Check if current fragment is a class tag.
|
246
|
+
// Looks the attributes and text inside the class tag.
|
247
|
+
innerMatch = match[i].match(/<\s*class=["|']([^"|']+)["|']\s*\>([^>]+)<\s*\/class\s*\>/);
|
248
|
+
|
249
|
+
classDefinition = this.getClass(innerMatch[1]);
|
250
|
+
/*
|
251
|
+
* Loop the class properties.
|
252
|
+
*/
|
253
|
+
for (atribute in classDefinition) {
|
254
|
+
switch (atribute) {
|
255
|
+
case "font":
|
256
|
+
proFont = classDefinition[atribute];
|
257
|
+
break;
|
258
|
+
case "fontFamily":
|
259
|
+
proFont.family = classDefinition[atribute];
|
260
|
+
break;
|
261
|
+
case "fontWeight":
|
262
|
+
proFont.weight = classDefinition[atribute];
|
263
|
+
break;
|
264
|
+
case "fontSize":
|
265
|
+
proFont.size = classDefinition[atribute];
|
266
|
+
break;
|
267
|
+
case "fontStyle":
|
268
|
+
proFont.style = classDefinition[atribute];
|
269
|
+
break;
|
270
|
+
case "fontColor":
|
271
|
+
if (this.isHex(classDefinition[atribute])) {
|
272
|
+
proColor = classDefinition[atribute];
|
273
|
+
}
|
274
|
+
break;
|
275
|
+
case "textShadow":
|
276
|
+
proShadow = this.trim(classDefinition[atribute]);
|
277
|
+
proShadow = proShadow.split(" ");
|
278
|
+
if (proShadow.length != 4) {
|
279
|
+
proShadow = null;
|
280
|
+
}
|
281
|
+
break;
|
282
|
+
}
|
283
|
+
}
|
284
|
+
proText = innerMatch[2];
|
285
|
+
} else if (/<\s*br\s*\/>/i.test(match[i])) {
|
286
|
+
// Check if current fragment is a line break.
|
287
|
+
y += parseInt(this.lineHeight, 10) * 1.5;
|
288
|
+
x = textInfo.x;
|
289
|
+
continue;
|
290
|
+
} else {
|
291
|
+
// Text without special style.
|
292
|
+
proText = match[i];
|
293
|
+
}
|
294
|
+
|
295
|
+
// Set the text Baseline
|
296
|
+
this.bufferContext.textBaseline = this.textBaseline;
|
297
|
+
// Set the text align
|
298
|
+
this.bufferContext.textAlign = this.textAlign;
|
299
|
+
// Font styles.
|
300
|
+
if (proFont instanceof Array) {
|
301
|
+
this.bufferContext.font = proFont.style + " " + proFont.weight + " " + proFont.size + " " + proFont.family;
|
302
|
+
} else {
|
303
|
+
this.bufferContext.font = proFont;
|
304
|
+
}
|
305
|
+
this.bufferContext.font = proFont;
|
306
|
+
// Set the color.
|
307
|
+
this.bufferContext.fillStyle = proColor;
|
308
|
+
// Set the Shadow.
|
309
|
+
if (proShadow != null) {
|
310
|
+
this.bufferContext.shadowOffsetX = proShadow[0].replace("px", "");
|
311
|
+
this.bufferContext.shadowOffsetY = proShadow[1].replace("px", "");
|
312
|
+
this.bufferContext.shadowBlur = proShadow[2].replace("px", "");
|
313
|
+
this.bufferContext.shadowColor = proShadow[3].replace("px", "");
|
314
|
+
}
|
315
|
+
|
316
|
+
// Reset textLines;
|
317
|
+
textLines = [];
|
318
|
+
// Clear javascript code line breaks.
|
319
|
+
proText = proText.replace(/\s*\n\s*/g, " ");
|
320
|
+
|
321
|
+
// Automatic Line break
|
322
|
+
if (boxWidth !== undefined) {
|
323
|
+
// If returns true, it means we need a line break.
|
324
|
+
if (this.checkLineBreak(proText, boxWidth, x)) {
|
325
|
+
// Split text by words.
|
326
|
+
splittedText = this.trim(proText).split(" ");
|
327
|
+
|
328
|
+
// If there's only one word we don't need to make more checks.
|
329
|
+
if (splittedText.length == 1) {
|
330
|
+
textLines.push({text: this.trim(proText) + " ", linebreak: true});
|
331
|
+
} else {
|
332
|
+
// Reset vars.
|
333
|
+
xAux = x;
|
334
|
+
var line = 0;
|
335
|
+
textLines[line] = {text: undefined, linebreak: false};
|
336
|
+
|
337
|
+
// Loop words.
|
338
|
+
for (k = 0; k < splittedText.length; k++) {
|
339
|
+
splittedText[k] += " ";
|
340
|
+
// Check if the current text fits into the current line.
|
341
|
+
if (!this.checkLineBreak(splittedText[k], boxWidth, xAux)) {
|
342
|
+
// Current text fit into the current line. So we save it
|
343
|
+
// to the current textLine.
|
344
|
+
if (textLines[line].text == undefined) {
|
345
|
+
textLines[line].text = splittedText[k];
|
346
|
+
} else {
|
347
|
+
textLines[line].text += splittedText[k];
|
348
|
+
}
|
349
|
+
|
350
|
+
xAux += this.bufferContext.measureText(splittedText[k]).width;
|
351
|
+
} else {
|
352
|
+
// Current text doesn't fit into the current line.
|
353
|
+
// We are doing a line break, so we reset xAux
|
354
|
+
// to initial x value.
|
355
|
+
xAux = textInfo.x;
|
356
|
+
if (textLines[line].text !== undefined) {
|
357
|
+
line++;
|
358
|
+
}
|
359
|
+
|
360
|
+
textLines[line] = {text: splittedText[k], linebreak: true};
|
361
|
+
xAux += this.bufferContext.measureText(splittedText[k]).width;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
// if textLines.length == 0 it means we doesn't need a linebreak.
|
369
|
+
if (textLines.length == 0) {
|
370
|
+
textLines.push({text: this.trim(proText) + " ", linebreak: false});
|
371
|
+
}
|
372
|
+
|
373
|
+
// Let's draw the text
|
374
|
+
for (n = 0; n < textLines.length; n++) {
|
375
|
+
// Start a new line.
|
376
|
+
if (textLines[n].linebreak) {
|
377
|
+
y += parseInt(this.lineHeight, 10);
|
378
|
+
x = textInfo.x;
|
379
|
+
}
|
380
|
+
this.bufferContext.fillText(textLines[n].text, x, y);
|
381
|
+
// Increment X position based on current text measure.
|
382
|
+
x += this.bufferContext.measureText(textLines[n].text).width;
|
383
|
+
}
|
384
|
+
|
385
|
+
this.bufferContext.restore();
|
386
|
+
}
|
387
|
+
};
|
388
|
+
|
389
|
+
/**
|
390
|
+
* Save a new class definition.
|
391
|
+
*/
|
392
|
+
this.defineClass = function (id, definition) {
|
393
|
+
// A simple check.
|
394
|
+
if (typeof (definition) != "object") {
|
395
|
+
alert("¡Invalid class!");
|
396
|
+
return false;
|
397
|
+
}
|
398
|
+
// Another simple check.
|
399
|
+
if (this.isEmpty(id)) {
|
400
|
+
alert("You must specify a Class Name.");
|
401
|
+
return false;
|
402
|
+
}
|
403
|
+
|
404
|
+
// Save it.
|
405
|
+
this.savedClasses[id] = definition;
|
406
|
+
return true;
|
407
|
+
};
|
408
|
+
|
409
|
+
/**
|
410
|
+
* Returns a saved class.
|
411
|
+
*/
|
412
|
+
this.getClass = function (id) {
|
413
|
+
if (this.savedClasses[id] !== undefined) {
|
414
|
+
return this.savedClasses[id];
|
415
|
+
}
|
416
|
+
};
|
417
|
+
|
418
|
+
this.getCanvas = function () {
|
419
|
+
// We need a valid ID
|
420
|
+
if (this.canvasId == null) {
|
421
|
+
alert("You must specify the canvas ID!");
|
422
|
+
return false;
|
423
|
+
}
|
424
|
+
// Let's save the Canvas into our class property...
|
425
|
+
this.canvas = document.getElementById(this.canvasId);
|
426
|
+
// ... and save its context too.
|
427
|
+
this.context = this.canvas.getContext('2d');
|
428
|
+
this.getBufferCanvas();
|
429
|
+
|
430
|
+
return true;
|
431
|
+
};
|
432
|
+
|
433
|
+
this.getBufferCanvas = function () {
|
434
|
+
// We will draw the text into the cache canvas
|
435
|
+
this.bufferCanvas = document.createElement('canvas');
|
436
|
+
this.bufferCanvas.width = this.canvas.width;
|
437
|
+
this.bufferCanvas.height = this.canvas.height;
|
438
|
+
this.bufferContext = this.bufferCanvas.getContext('2d');
|
439
|
+
};
|
440
|
+
/**
|
441
|
+
* Check if the cache canvas exist.
|
442
|
+
*/
|
443
|
+
this.getCache = function (id) {
|
444
|
+
if (this.cacheCanvas[id] === undefined) {
|
445
|
+
return false;
|
446
|
+
} else {
|
447
|
+
return true;
|
448
|
+
}
|
449
|
+
};
|
450
|
+
/**
|
451
|
+
* We create a new canvas element for each cache element.
|
452
|
+
*/
|
453
|
+
this.setCache = function (id) {
|
454
|
+
this.cacheCanvas[id] = document.createElement("canvas");
|
455
|
+
this.cacheCanvas[id].width = this.bufferCanvas.width;
|
456
|
+
this.cacheCanvas[id].height = this.bufferCanvas.height;
|
457
|
+
this.cacheContext[id] = this.cacheCanvas[id].getContext('2d');
|
458
|
+
this.cacheContext[id].drawImage(this.bufferCanvas, 0, 0);
|
459
|
+
};
|
460
|
+
/**
|
461
|
+
* Check if a line break is needed.
|
462
|
+
*/
|
463
|
+
this.checkLineBreak = function (text, boxWidth, x) {
|
464
|
+
return (this.bufferContext.measureText(text).width + x > boxWidth);
|
465
|
+
};
|
466
|
+
|
467
|
+
/**
|
468
|
+
* A simple function to validate a Hex code.
|
469
|
+
*/
|
470
|
+
this.isHex = function (hex) {
|
471
|
+
return (/^(#[a-fA-F0-9]{3,6})$/i.test(hex));
|
472
|
+
};
|
473
|
+
/**
|
474
|
+
* A simple function to check if the given value is a number.
|
475
|
+
*/
|
476
|
+
this.isNumber = function (n) {
|
477
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
478
|
+
};
|
479
|
+
/**
|
480
|
+
* A simple function to check if the given value is empty.
|
481
|
+
*/
|
482
|
+
this.isEmpty = function (str) {
|
483
|
+
// Remove white spaces.
|
484
|
+
str = str.replace(/^\s+|\s+$/, '');
|
485
|
+
return str.length == 0;
|
486
|
+
};
|
487
|
+
/**
|
488
|
+
* A simple function clear whitespaces.
|
489
|
+
*/
|
490
|
+
this.trim = function (str) {
|
491
|
+
var ws, i;
|
492
|
+
str = str.replace(/^\s\s*/, '');
|
493
|
+
ws = /\s/;
|
494
|
+
i = str.length;
|
495
|
+
while (ws.test(str.charAt(--i))) {
|
496
|
+
continue;
|
497
|
+
}
|
498
|
+
return str.slice(0, i + 1);
|
499
|
+
};
|
500
|
+
}
|