라이브러리 추가(이미지편집기, 날짜 입력기)

main
이범준 1 year ago
parent ef30fcb4c3
commit 9448c58b27

@ -0,0 +1 @@
[[1,1,1],[1,1,0.8],[1,1,0.6],[1,1,0.4],[1,1,0.2],[1,1,0],[1,0.8,1],[1,0.8,0.8],[1,0.8,0.6],[1,0.8,0.4],[1,0.8,0.2],[1,0.8,0],[1,0.6,1],[1,0.6,0.8],[1,0.6,0.6],[1,0.6,0.4],[1,0.6,0.2],[1,0.6,0],[1,0.4,1],[1,0.4,0.8],[1,0.4,0.6],[1,0.4,0.4],[1,0.4,0.2],[1,0.4,0],[1,0.2,1],[1,0.2,0.8],[1,0.2,0.6],[1,0.2,0.4],[1,0.2,0.2],[1,0.2,0],[1,0,1],[1,0,0.8],[1,0,0.6],[1,0,0.4],[1,0,0.2],[1,0,0],[0.8,1,1],[0.8,1,0.8],[0.8,1,0.6],[0.8,1,0.4],[0.8,1,0.2],[0.8,1,0],[0.8,0.8,1],[0.8,0.8,0.8],[0.8,0.8,0.6],[0.8,0.8,0.4],[0.8,0.8,0.2],[0.8,0.8,0],[0.8,0.6,1],[0.8,0.6,0.8],[0.8,0.6,0.6],[0.8,0.6,0.4],[0.8,0.6,0.2],[0.8,0.6,0],[0.8,0.4,1],[0.8,0.4,0.8],[0.8,0.4,0.6],[0.8,0.4,0.4],[0.8,0.4,0.2],[0.8,0.4,0],[0.8,0.2,1],[0.8,0.2,0.8],[0.8,0.2,0.6],[0.8,0.2,0.4],[0.8,0.2,0.2],[0.8,0.2,0],[0.8,0,1],[0.8,0,0.8],[0.8,0,0.6],[0.8,0,0.4],[0.8,0,0.2],[0.8,0,0],[0.6,1,1],[0.6,1,0.8],[0.6,1,0.6],[0.6,1,0.4],[0.6,1,0.2],[0.6,1,0],[0.6,0.8,1],[0.6,0.8,0.8],[0.6,0.8,0.6],[0.6,0.8,0.4],[0.6,0.8,0.2],[0.6,0.8,0],[0.6,0.6,1],[0.6,0.6,0.8],[0.6,0.6,0.6],[0.6,0.6,0.4],[0.6,0.6,0.2],[0.6,0.6,0],[0.6,0.4,1],[0.6,0.4,0.8],[0.6,0.4,0.6],[0.6,0.4,0.4],[0.6,0.4,0.2],[0.6,0.4,0],[0.6,0.2,1],[0.6,0.2,0.8],[0.6,0.2,0.6],[0.6,0.2,0.4],[0.6,0.2,0.2],[0.6,0.2,0],[0.6,0,1],[0.6,0,0.8],[0.6,0,0.6],[0.6,0,0.4],[0.6,0,0.2],[0.6,0,0],[0.4,1,1],[0.4,1,0.8],[0.4,1,0.6],[0.4,1,0.4],[0.4,1,0.2],[0.4,1,0],[0.4,0.8,1],[0.4,0.8,0.8],[0.4,0.8,0.6],[0.4,0.8,0.4],[0.4,0.8,0.2],[0.4,0.8,0],[0.4,0.6,1],[0.4,0.6,0.8],[0.4,0.6,0.6],[0.4,0.6,0.4],[0.4,0.6,0.2],[0.4,0.6,0],[0.4,0.4,1],[0.4,0.4,0.8],[0.4,0.4,0.6],[0.4,0.4,0.4],[0.4,0.4,0.2],[0.4,0.4,0],[0.4,0.2,1],[0.4,0.2,0.8],[0.4,0.2,0.6],[0.4,0.2,0.4],[0.4,0.2,0.2],[0.4,0.2,0],[0.4,0,1],[0.4,0,0.8],[0.4,0,0.6],[0.4,0,0.4],[0.4,0,0.2],[0.4,0,0],[0.2,1,1],[0.2,1,0.8],[0.2,1,0.6],[0.2,1,0.4],[0.2,1,0.2],[0.2,1,0],[0.2,0.8,1],[0.2,0.8,0.8],[0.2,0.8,0.6],[0.2,0.8,0.4],[0.2,0.8,0.2],[0.2,0.8,0],[0.2,0.6,1],[0.2,0.6,0.8],[0.2,0.6,0.6],[0.2,0.6,0.4],[0.2,0.6,0.2],[0.2,0.6,0],[0.2,0.4,1],[0.2,0.4,0.8],[0.2,0.4,0.6],[0.2,0.4,0.4],[0.2,0.4,0.2],[0.2,0.4,0],[0.2,0.2,1],[0.2,0.2,0.8],[0.2,0.2,0.6],[0.2,0.2,0.4],[0.2,0.2,0.2],[0.2,0.2,0],[0.2,0,1],[0.2,0,0.8],[0.2,0,0.6],[0.2,0,0.4],[0.2,0,0.2],[0.2,0,0],[0,1,1],[0,1,0.8],[0,1,0.6],[0,1,0.4],[0,1,0.2],[0,1,0],[0,0.8,1],[0,0.8,0.8],[0,0.8,0.6],[0,0.8,0.4],[0,0.8,0.2],[0,0.8,0],[0,0.6,1],[0,0.6,0.8],[0,0.6,0.6],[0,0.6,0.4],[0,0.6,0.2],[0,0.6,0],[0,0.4,1],[0,0.4,0.8],[0,0.4,0.6],[0,0.4,0.4],[0,0.4,0.2],[0,0.4,0],[0,0.2,1],[0,0.2,0.8],[0,0.2,0.6],[0,0.2,0.4],[0,0.2,0.2],[0,0.2,0],[0,0,1],[0,0,0.8],[0,0,0.6],[0,0,0.4],[0,0,0.2],[0.9333333333333333,0,0],[0.8666666666666667,0,0],[0.7333333333333333,0,0],[0.6666666666666666,0,0],[0.5333333333333333,0,0],[0.4666666666666667,0,0],[0.3333333333333333,0,0],[0.26666666666666666,0,0],[0.13333333333333333,0,0],[0.06666666666666667,0,0],[0,0.9333333333333333,0],[0,0.8666666666666667,0],[0,0.7333333333333333,0],[0,0.6666666666666666,0],[0,0.5333333333333333,0],[0,0.4666666666666667,0],[0,0.3333333333333333,0],[0,0.26666666666666666,0],[0,0.13333333333333333,0],[0,0.06666666666666667,0],[0,0,0.9333333333333333],[0,0,0.8666666666666667],[0,0,0.7333333333333333],[0,0,0.6666666666666666],[0,0,0.5333333333333333],[0,0,0.4666666666666667],[0,0,0.3333333333333333],[0,0,0.26666666666666666],[0,0,0.13333333333333333],[0,0,0.06666666666666667],[0.9333333333333333,0.9333333333333333,0.9333333333333333],[0.8666666666666667,0.8666666666666667,0.8666666666666667],[0.7333333333333333,0.7333333333333333,0.7333333333333333],[0.6666666666666666,0.6666666666666666,0.6666666666666666],[0.5333333333333333,0.5333333333333333,0.5333333333333333],[0.4666666666666667,0.4666666666666667,0.4666666666666667],[0.3333333333333333,0.3333333333333333,0.3333333333333333],[0.26666666666666666,0.26666666666666666,0.26666666666666666],[0.13333333333333333,0.13333333333333333,0.13333333333333333],[0.06666666666666667,0.06666666666666667,0.06666666666666667],[0,0,0]]

@ -0,0 +1 @@
[[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,0.8],[0.8,1,1],[1,0.6,0.6],[1,0.6,0.6],[1,0.6,0.6],[1,0.6,0.6],[1,0.8,0.6],[1,1,0.6],[0.8,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.6],[0.6,1,0.8],[0.6,1,1],[1,0.4,0.4],[1,0.4,0.4],[1,0.4,0.4],[1,0.6,0.4],[1,0.8,0.4],[1,1,0.4],[0.8,1,0.4],[0.6,1,0.4],[0.4,1,0.4],[0.4,1,0.4],[0.4,1,0.4],[0.4,1,0.4],[0.4,1,0.4],[0.4,1,0.6],[0.4,1,0.8],[0.4,1,1],[1,0.2,0.2],[1,0.2,0.2],[1,0.4,0.2],[1,0.6,0.2],[1,0.8,0.2],[1,1,0.2],[0.8,1,0.2],[0.6,1,0.2],[0.4,1,0.2],[0.2,1,0.2],[0.2,1,0.2],[0.2,1,0.2],[0.2,1,0.4],[0.2,1,0.6],[0.2,1,0.8],[0.2,1,1],[1,0,0],[1,0.2,0],[1,0.4,0],[1,0.6,0],[1,0.8,0],[1,1,0],[0.8,1,0],[0.6,1,0],[0.2,1,0],[0.2,1,0],[0,1,0],[0,1,0.2],[0,1,0.4],[0,1,0.6],[0,1,0.8],[0,1,1],[0.8,0,0],[0.8,0.2,0],[0.8,0.4,0],[0.8,0.6,0],[0.8,0.8,0],[0.8,0.8,0],[0.8,0.8,0],[0.6,0.8,0],[0.4,0.8,0],[0.2,0.8,0],[0,0.8,0],[0,0.8,0.2],[0,0.8,0.4],[0,0.8,0.6],[0,0.8,0.8],[0,0.8,0.8],[0.6,0,0],[0.6,0.2,0],[0.6,0.4,0],[0.6,0.6,0],[0.6,0.6,0],[0.6,0.6,0],[0.6,0.6,0],[0.6,0.6,0],[0.4,0.6,0],[0.2,0.6,0],[0,0.6,0],[0,0.6,0.2],[0,0.6,0.4],[0,0.6,0.6],[0,0.6,0.6],[0,0.6,0.6],[0.4,0,0],[0.4,0.2,0],[0.4,0.4,0],[0.4,0.4,0],[0.4,0.4,0],[0.4,0.4,0],[0.4,0.4,0],[0.4,0.4,0],[0.4,0.4,0],[0.2,0.4,0],[0,0.4,0],[0,0.4,0.2],[0,0.4,0.4],[0,0.4,0.4],[0,0.4,0.4],[0,0.4,0.4],[0.2,0,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0.2,0.2,0],[0,0.2,0],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0.2,0,0],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0.2,0,0.2],[0,0,0.2],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0,0.2,0.2],[0.4,0,0],[0.4,0,0.2],[0.4,0,0.4],[0.4,0,0.4],[0.4,0,0.4],[0.4,0,0.4],[0.4,0,0.4],[0.4,0,0.4],[0.4,0,0.4],[0.2,0,0.4],[0,0,0.4],[0,0.2,0.4],[0,0.4,0.4],[0,0.4,0.4],[0,0.4,0.4],[0,0.4,0.4],[0.6,0,0],[0.6,0,0.2],[0.6,0,0.4],[0.6,0,0.6],[0.6,0,0.6],[0.6,0,0.6],[0.6,0,0.6],[0.6,0,0.6],[0.4,0,0.6],[0.2,0,0.6],[0,0,0.6],[0,0.2,0.6],[0,0.4,0.6],[0,0.6,0.6],[0,0.6,0.6],[0,0.6,0.6],[0.8,0,0],[0.8,0,0.2],[0.8,0,0.4],[0.8,0,0.6],[0.8,0,0.8],[0.8,0,0.8],[0.8,0,0.8],[0.6,0,0.8],[0.4,0,0.8],[0.2,0,0.8],[0,0,0.8],[0,0.2,0.8],[0,0.4,0.8],[0,0.6,0.8],[0,0.8,0.8],[0,0.8,0.8],[1,0,0],[1,0,0.2],[1,0,0.4],[1,0,0.6],[1,0,0.8],[1,0,1],[0.8,0,1],[0.6,0,1],[0.4,0,1],[0.2,0,1],[0,0,1],[0,0.2,1],[0,0.4,1],[0,0.6,1],[0,0.8,1],[0,1,1],[1,0.2,0.2],[1,0.2,0.2],[1,0.2,0.4],[1,0.2,0.6],[1,0.2,0.8],[1,0.2,1],[0.8,0.2,1],[0.6,0.2,1],[0.4,0.2,1],[0.2,0.2,1],[0.2,0.2,1],[0.2,0.2,1],[0.2,0.4,1],[0.2,0.6,1],[0.2,0.8,1],[0.2,1,1],[1,0.4,0.4],[1,0.4,0.4],[1,0.4,0.4],[1,0.4,0.6],[1,0.4,0.8],[1,0.4,1],[0.8,0.4,1],[0.6,0.4,1],[0.4,0.4,1],[0.4,0.4,1],[0.4,0.4,1],[0.4,0.4,1],[0.4,0.4,1],[0.4,0.6,1],[0.4,0.8,1],[0.4,1,1],[1,0.6,0.6],[1,0.6,0.6],[1,0.6,0.6],[1,0.6,0.6],[1,0.6,0.8],[1,0.6,1],[0.8,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.6,1],[0.6,0.8,1],[0.6,1,1],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,0.8],[1,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,0.8,1],[0.8,1,1],[0,0,0],[0.2,0.2,0.2],[0.4,0.4,0.4],[0.6,0.6,0.6],[0.8,0.8,0.8],[1,1,1],[0.8,0.6,0.6],[0.8,0.4,0.4],[0.8,0.2,0.2],[0.6,0.2,0.2],[0.4,0.2,0.2],[0.8,0.4,0.2],[0.8,0.4,0.2],[0.8,0.6,0.4],[0.8,0.6,0.2],[0.8,0.8,0.2],[0.6,0.6,0.4],[0.6,0.6,0.2],[0.8,0.8,0.4],[0.6,0.8,0.4],[0.4,0.6,0.2],[0.4,0.8,0.2],[0.4,0.8,0.4],[0.4,0.6,0.4],[0.4,0.6,0.4],[0.2,0.6,0.2],[0.2,0.4,0.2],[0.2,0.8,0.4],[0.2,0.8,0.4],[0.2,0.8,0.6],[0.2,0.8,0.6],[0.2,0.8,0.8],[0.6,0.8,0.8],[0.2,0.6,0.6],[0.4,0.8,0.8],[0.2,0.4,0.4],[0.2,0.4,0.6],[0.4,0.4,0.6],[0.4,0.4,0.8],[0.4,0.4,0.8],[0.2,0.2,0.6],[0.4,0.2,0.8],[0.4,0.2,0.6],[0.6,0.4,0.8],[0.6,0.2,0.8],[0.8,0.6,0.8],[0.4,0.2,0.4],[0.6,0.4,0.6],[0.8,0.2,0.8],[0.8,0.2,0.6],[0.8,0.4,0.6],[0.6,0.2,0.4],[0.8,0.2,0.4]]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,363 @@
/*
* Copyright (C) 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-10-29 19:05:49 +0200 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the integration code for PaintWeb inside <a
* href="http://www.moodle.org">Moodle</a>.
*/
/**
* @class The Moodle extension for PaintWeb. This extension handles the Moodle
* integration inside the PaintWeb code.
*
* <p><strong>Note:</strong> This extension is supposed to work with Moodle 1.9
* and Moodle 2.0.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.extensions.moodle = function (app) {
var _self = this,
appEvent = pwlib.appEvent,
config = app.config,
gui = app.gui,
lang = app.lang.moodle,
moodleServer = config.moodleServer,
tinymceEditor = null,
qfErrorShown = false;
// Holds information related to Moodle.
var moodleInfo = {
// Holds the URL of the image the user is saving.
imageURL: null,
// The class name for the element which holds the textarea buttons (toggle
// on/off).
// This element exists only in Moodle 1.9.
textareaButtons: 'textareaicons',
// The image save handler script on the server-side. The path is relative to
// the PaintWeb base folder.
// This used with Moodle 2.0.
imageSaveHandler20: '../ext/moodle/imagesave20.php',
// The image save handler script for Moodle 1.9.
imageSaveHandler19: '../ext/moodle/imagesave19.php',
// This holds the release version of Moodle being used. This should be 1.9
// or 2.0.
release: 0,
// Moodle 2.0 draft item ID used for file storage.
draftitemid: null
};
/**
* The <code>extensionRegister</code> event handler. Setup event listeners,
* determine Moodle version, and more.
*
* @returns {Boolean} True if the extension initialized successfully, or false
* if not.
*/
this.extensionRegister = function () {
// Register application events.
app.events.add('guiShow', this.guiShow);
app.events.add('guiHide', this.guiHide);
app.events.add('imageSave', this.imageSave);
if (moodleServer && moodleServer.release) {
var matches = moodleServer.release.match(/^\s*(\d+\.\d+)/);
if (matches && matches[1]) {
moodleInfo.release = parseFloat(matches[1]);
}
}
if (typeof window.qf_errorHandler === 'function' && config.tinymce &&
!config.tinymce.onSubmitUnsaved) {
config.tinymce.onSubmitUnsaved = this.onSubmitUnsaved;
}
return true;
};
/**
* The <code>submit</code> event handler for the form to which the PaintWeb
* instance is attached to. This method is invoked by the TinyMCE plugin when
* the form is submitted while the user edits an image with unsaved changes.
* @private
*/
this.onSubmitUnsaved = function () {
var tmce = config.tinymceEditor,
textarea = tmce ? tmce.getElement() : null,
guiPlaceholder = config.guiPlaceholder,
prevSibling = guiPlaceholder.previousSibling;
if (tmce && textarea && window.qf_errorHandler) {
try {
qf_errorHandler(textarea, "\n - " + lang.errorSubmitUnsaved);
} catch (err) {
return;
}
qfErrorShown = true;
// Due to the styling of the error shown by Moodle, PaintWeb must have
// clear:right.
if (prevSibling && prevSibling.className &&
prevSibling.className.indexOf('paintweb_tinymce_status') !== -1) {
prevSibling.style.clear = 'right';
} else {
guiPlaceholder.style.clear = 'right';
}
}
};
/**
* The <code>imageSave</code> application event handler. When the user
* attempts to save an image, this extension handles the event by sending the
* image data to the Moodle server, to perform the actual save operation.
*
* @private
* @param {pwlib.appEvent.imageSave} ev The application event object.
*/
this.imageSave = function (ev) {
if (!ev.dataURL) {
return;
}
ev.preventDefault();
moodleInfo.imageURL = config.imageLoad.src;
if (!moodleInfo.imageURL || moodleInfo.imageURL.substr(0, 5) === 'data:') {
moodleInfo.imageURL = '-';
}
if (config.moodleSaveMethod === 'dataURL') {
app.events.dispatch(new appEvent.imageSaveResult(true,
moodleInfo.imageURL, ev.dataURL));
} else {
var handlerURL = PaintWeb.baseFolder,
send = 'url=' + encodeURIComponent(moodleInfo.imageURL) +
'&dataURL=' + encodeURIComponent(ev.dataURL),
headers = {'Content-Type': 'application/x-www-form-urlencoded'};
// In Moodle 2.0 we include the context ID and the draft item ID, such
// that the image save script can properly save the new image inside the
// current draft area of the current textarea element.
if (moodleInfo.release >= 2) {
handlerURL += moodleInfo.imageSaveHandler20;
if (moodleServer.contextid) {
send += '&contextid=' + encodeURIComponent(moodleServer.contextid);
}
if (moodleInfo.draftitemid) {
send += '&draftitemid=' + encodeURIComponent(moodleInfo.draftitemid);
}
} else {
handlerURL += moodleInfo.imageSaveHandler19;
}
pwlib.xhrLoad(handlerURL, imageSaveReady, 'POST', send, headers);
}
};
/**
* The image save <code>onreadystatechange</code> event handler for the
* <code>XMLHttpRequest</code> which performs the image save. This function
* uses the reply to determine if the image save operation is successful or
* not.
*
* <p>The {@link pwlib.appEvent.imageSaveResult} application event is
* dispatched.
*
* <p>The server-side script must reply with a JSON object with the following
* properties:
*
* <ul>
* <li><var>successful</var> which tells if the image save operation was
* successful or not;
*
* <li><var>url</var> which must tell the same URL as the image we just
* saved (sanity/security check);
*
* <li><var>urlNew</var> is optional. This allows the server-side script to
* change the image URL;
*
* <li><var>errorMessage</var> is optional. When the image save was not
* successful, an error message can be displayed.
* </ul>
*
* @private
* @param {XMLHttpRequest} xhr The XMLHttpRequest object.
*/
function imageSaveReady (xhr) {
if (!xhr || xhr.readyState !== 4) {
return;
}
var result = {successful: false, url: moodleInfo.imageURL};
if ((xhr.status !== 304 && xhr.status !== 200) || !xhr.responseText) {
alert(lang.xhrRequestFailed);
app.events.dispatch(new appEvent.imageSaveResult(false, result.url, null,
lang.xhrRequestFailed));
return;
}
try {
result = JSON.parse(xhr.responseText);
} catch (err) {
result.errorMessage = lang.jsonParseFailed + "\n" + err;
alert(result.errorMessage);
}
if (result.successful) {
if (result.url !== moodleInfo.imageURL) {
alert(pwlib.strf(lang.urlMismatch, {
url: moodleInfo.imageURL,
urlServer: result.url || 'null'}));
}
} else {
if (result.errorMessage) {
alert(lang.imageSaveFailed + "\n" + result.errorMessage);
} else {
alert(lang.imageSaveFailed);
}
}
app.events.dispatch(new appEvent.imageSaveResult(result.successful,
result.url, result.urlNew, result.errorMessage));
};
/**
* The <code>guiShow</code> application event handler. When the PaintWeb GUI
* is shown, we must hide the textarea icons for the current textarea element,
* inside a Moodle page.
* @private
*/
this.guiShow = function () {
var pNode = config.guiPlaceholder.parentNode,
textareaButtons
= pNode.getElementsByClassName(moodleInfo.textareaButtons)[0];
// These show in Moodle 1.9.
if (textareaButtons) {
textareaButtons.style.display = 'none';
}
qfErrorShown = false;
// For Moodle 2.0 we must determine the draft item ID in order to properly
// perform the image save operation into the current draft area.
if (moodleInfo.release < 2) {
return;
}
// Typically the TinyMCE editor instance is attached to a textarea element
// which has a name=whatever[text] or similar form. In the same form as the
// textarea, there must be a hidden input element with the
// name=whatever[itemid]. The value of that input holds the draft item ID.
var tmce = config.tinymceEditor,
textarea = tmce ? tmce.getElement() : null,
frm = textarea ? textarea.form : null;
if (!tmce || !textarea || !textarea.name || !frm) {
return;
}
var fieldname = textarea.name.replace(/\[text\]$/, '');
if (!fieldname) {
return;
}
var draftitemid = frm.elements.namedItem(fieldname + '[itemid]'),
format = frm.elements.namedItem(fieldname + '[format]');
if (draftitemid) {
moodleInfo.draftitemid = draftitemid.value;
}
if (format) {
format.style.display = 'none';
}
};
/**
* The <code>guiHide</code> application event handler. When the PaintWeb GUI
* is hidden, we must show again the textarea icons for the current textarea
* element, inside a Moodle page.
* @private
*/
this.guiHide = function () {
var guiPlaceholder = config.guiPlaceholder,
prevSibling = guiPlaceholder.previousSibling;
pNode = guiPlaceholder.parentNode,
textareaButtons
= pNode.getElementsByClassName(moodleInfo.textareaButtons)[0];
// These show in Moodle 1.9.
if (textareaButtons) {
textareaButtons.style.display = '';
}
var tmce = config.tinymceEditor,
textarea = tmce ? tmce.getElement() : null,
frm = textarea ? textarea.form : null;
if (!tmce || !textarea || !textarea.name || !frm) {
return;
}
if (qfErrorShown) {
if (window.qf_errorHandler) {
qf_errorHandler(textarea, '');
}
if (prevSibling && prevSibling.className &&
prevSibling.className.indexOf('paintweb_tinymce_status') !== -1) {
prevSibling.style.clear = '';
} else {
guiPlaceholder.style.clear = '';
}
}
// The format input element only shows in Moodle 2.0.
if (moodleInfo.release >= 2) {
var fieldname = textarea.name.replace(/\[text\]$/, '');
if (!fieldname) {
return;
}
var format = frm.elements.namedItem(fieldname + '[format]');
if (format) {
format.style.display = '';
}
}
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,375 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-06-16 21:56:40 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Allows users to draw in PaintWeb using the keyboard, without
* any pointing device.
*/
/**
* @class The MouseKeys extension.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.extensions.mousekeys = function (app) {
var _self = this,
canvas = app.buffer.canvas,
config = app.config,
container = app.gui.elems.canvasContainer,
doc = app.doc,
gui = app.gui,
image = app.image,
MathCeil = Math.ceil,
mouse = app.mouse,
tool = app.tool || {};
/**
* Holds the current mouse movement speed in pixels.
*
* @private
* @type Number
*/
var speed = 1;
/**
* Holds the current mouse movement acceleration, taken from the
* configuration.
*
* @private
* @type Number
* @see PaintWeb.config.mousekeys.accel The mouse keys acceleration setting.
*/
var accel = 0.1;
/**
* Holds a reference to the DOM element representing the pointer on top of the
* canvas element.
*
* @private
* @type Element
*/
var pointer = null;
var pointerStyle = null;
/**
* The <code>extensionRegister</code> event handler. This initializes the
* extension by adding the pointer DOM element and by setting up the keyboard
* shortcuts.
*
* @returns {Boolean} True if the extension initialized successfully, or false
* if not.
*/
this.extensionRegister = function () {
accel = config.mousekeys.accel;
pointer = doc.createElement('div');
if (!pointer) {
return false;
}
pointerStyle = pointer.style;
pointer.className = gui.classPrefix + 'mousekeysPointer';
pointerStyle.display = 'none';
container.appendChild(pointer);
canvas.addEventListener('mousemove', pointerMousemove, false);
var action, keys, i, n, result = {};
for (action in config.mousekeys.actions) {
keys = config.mousekeys.actions[action];
for (i = 0, n = keys.length; i < n; i++) {
result[keys[i]] = {'extension': _self._id, 'action': action};
}
};
pwlib.extend(config.keys, result);
return true;
};
/**
* The <code>extensionUnregister</code> event handler. This will remove the
* pointer DOM element and the canvas event listener.
*/
this.extensionUnregister = function () {
container.removeChild(pointer);
canvas.removeEventListener('mousemove', pointerMousemove, false);
var key, kobj;
for (key in config.keys) {
kobj = config.keys[key];
if (kobj.extension === _self._id) {
delete config.keys[key];
}
}
};
/**
* Track the virtual pointer coordinates, by updating the position of the
* <var>pointer</var> element. This allows the keyboard users to see where
* they moved the virtual pointer.
*
* @param {Event} ev The DOM Event object.
*/
function pointerMousemove (ev) {
if (!('kobj_' in ev) || !('extension' in ev.kobj_) ||
ev.kobj_.extension !== _self._id) {
if (pointerStyle.display === 'block') {
pointerStyle.display = 'none';
}
}
};
/**
* The <code>keydown</code> event handler.
*
* <p>This method requires a DOM Event object which has the
* <var>ev.kobj_</var> object reference from the keyboard shortcuts
* configuration. The <var>kobj_</var> object must have the <var>action</var>
* property. Support for the "ButtonToggle" and the "ButtonClick" actions is
* implemented.
*
* <p>The "ButtonToggle" action essentially means that a mouse event will be
* generated, either <code>mousedown</code> or <code>mouseup</code>. By
* alternating these two events, this method allows the user to start and stop
* the drawing operation at any moment using the keyboard shortcut they have
* configured.
*
* <p>Under typical usage, the "ButtonClick" action translates the
* <code>keydown</code> event to <code>mousedown</code>. The
* <code>keyup</code> event handler will also fire the <code>mouseup</code>
* event. This allows the user to simulate holding down the mouse button,
* while he/she holds down a key.
*
* <p>A <code>click</code> event is always fired after the firing of
* a <code>mouseup</code> event.
*
* <p>Irrespective of the key the user pressed, this method does always reset
* the speed and acceleration of the pointer movement.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the keyboard shortcut was recognized, or false
* if not.
*
* @see PaintWeb.config.mousekeys.actions The keyboard shortcuts configuration
* object.
*/
this.keydown = function (ev) {
speed = 1;
accel = config.mousekeys.accel;
if (pointerStyle.display === 'none') {
pointerStyle.display = 'block';
pointerStyle.top = (mouse.y * image.canvasScale) + 'px';
pointerStyle.left = (mouse.x * image.canvasScale) + 'px';
if (mouse.buttonDown) {
pointer.className += ' ' + gui.classPrefix + 'mouseDown';
} else {
pointer.className = pointer.className.replace(' ' + gui.classPrefix
+ 'mouseDown', '');
}
}
tool = app.tool || {};
switch (ev.kobj_.action) {
case 'ButtonToggle':
if (mouse.buttonDown) {
mouse.buttonDown = false;
if ('mouseup' in tool) {
tool.mouseup(ev);
}
if ('click' in tool) {
tool.click(ev);
}
} else {
mouse.buttonDown = true;
if ('mousedown' in tool) {
tool.mousedown(ev);
}
}
break;
case 'ButtonClick':
if (!mouse.buttonDown) {
mouse.buttonDown = true;
if ('mousedown' in tool) {
tool.mousedown(ev);
}
}
break;
default:
return false;
}
if (mouse.buttonDown) {
pointer.className += ' ' + gui.classPrefix + 'mouseDown';
} else {
pointer.className = pointer.className.replace(' ' + gui.classPrefix
+ 'mouseDown', '');
}
return true;
};
/**
* The <code>keypress</code> event handler.
*
* <p>This method requires a DOM Event object with a <var>ev.kobj_</var>
* object reference to the keyboard shortcut configuration. The keyboard
* shortcut configuration object must have the <var>action</var> property.
*
* <p>This event handler implements support for the following <var>param</var>
* values: "SouthWest", "South", "SouthEast", "West", "East", "NorthWest",
* "North" and "NorthEast", All of these values indicate the movement
* direction. This method generates synthetic <var>movemove</var> events based
* on the direction desired, effectively emulating the use of a real pointing
* device.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the keyboard shortcut was recognized, or false
* if not.
*
* @see PaintWeb.config.mousekeys.actions The keyboard shortcuts configuration
* object.
*/
this.keypress = function (ev) {
if (ev.shiftKey) {
speed += speed * accel * 3;
} else {
speed += speed * accel;
}
var step = MathCeil(speed);
switch (ev.kobj_.action) {
case 'SouthWest':
mouse.x -= step;
mouse.y += step;
break;
case 'South':
mouse.y += step;
break;
case 'SouthEast':
mouse.x += step;
mouse.y += step;
break;
case 'West':
mouse.x -= step;
break;
case 'East':
mouse.x += step;
break;
case 'NorthWest':
mouse.x -= step;
mouse.y -= step;
break;
case 'North':
mouse.y -= step;
break;
case 'NorthEast':
mouse.x += step;
mouse.y -= step;
break;
default:
return false;
}
if (mouse.x < 0) {
mouse.x = 0;
} else if (mouse.x > image.width) {
mouse.x = image.width;
}
if (mouse.y < 0) {
mouse.y = 0;
} else if (mouse.y > image.height) {
mouse.y = image.height;
}
pointerStyle.top = (mouse.y * image.canvasScale) + 'px';
pointerStyle.left = (mouse.x * image.canvasScale) + 'px';
if ('mousemove' in tool) {
tool.mousemove(ev);
}
return true;
};
/**
* The <code>keyup</code> event handler.
*
* <p>This method requires a DOM Event object which has the
* <var>ev.kobj_</var> object reference from the keyboard shortcuts
* configuration. The <var>kobj_</var> object must have the <var>action</var>
* property. Support for the "ButtonClick" action is implemented.
*
* <p>Under typical usage, the "ButtonClick" action translates the
* <code>keydown</code> event to <code>mousedown</code>. This event handler
* fires the <code>mouseup</code> event. This allows the user to simulate
* holding down the mouse button, while he/she holds down a key.
*
* <p>A <code>click</code> event is always fired after the firing of the
* <code>mouseup</code> event.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the keyboard shortcut was recognized, or false
* if not.
*
* @see PaintWeb.config.mousekeys.actions The keyboard shortcuts configuration
* object.
*/
this.keyup = function (ev) {
if (ev.kobj_.action == 'ButtonClick' && mouse.buttonDown) {
mouse.buttonDown = false;
if ('mouseup' in tool) {
tool.mouseup(ev);
}
if ('click' in tool) {
tool.click(ev);
}
pointer.className = pointer.className.replace(' ' + gui.classPrefix
+ 'mouseDown', '');
return true;
}
return false;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

File diff suppressed because one or more lines are too long

@ -0,0 +1,451 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-05-11 19:37:56 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Minimal code used for aiding debugging PaintWeb.
*/
// Opera compatibility
if (!window.console) {
/**
* @namespace Holds a simple method used for debugging. Opera doesn't have the
* window.console API like Firefox+Firebug has.
*/
window.console = {};
}
if (!window.console.log) {
/**
* Display a message in the debugger. If available, opera.postError is used,
* otherwise no message is displayed.
*
* @param {mixed} mixed Any number of arguments, each one is displayed in the
* debugger.
*/
window.console.log = function () {
var msg = '';
for (var i = 0, n = arguments.length; i < n; i++) {
msg += arguments[i] + ' ';
}
if (window.opera && window.opera.postError) {
opera.postError(msg);
}
};
}
if (!window.console.warn) {
/**
* Display a message in the debugger. If available, opera.postError is used,
* otherwise no warning is displayed.
*
* @param {mixed} mixed Any number of arguments, each one is displayed in the
* debugger.
*/
window.console.warn = function () {
console.log.apply(null, arguments);
};
}
/**
* JavaScript code performance profiler.
* <p>Nested timers are accounted for - see the example below.
*
* @example
* <code>// To profile your code just do:
* var profiler = new $.profiler();
*
* profiler.start('long test');
* // ... more code ...
* profiler.start('function 1');
* // ... more code ...
* profiler.stop('function 1');
* // ... more code ...
* profiler.start('function 2');
* // ... more code ...
* profiler.stop('function 2');
* // ... more code ...
* profiler.stop('long test');
*
* // To see the results do:
* profiler.reportText();
* // or ..
* profiler.reportData();</code>
*
* @class JavaScript code performance profiler.
*/
var libProfiler = function () {
/**
* @ignore
* @class Function timer. This is the constructor used for instancing a single
* timer object created by the profiler.
*
* @private
* @param {String} name_ The timer name.
* @param {Boolean} [independent_=false] Tells if the timer is independent.
* Means this timer will not affect the timers execution stack.
*/
function fnTimer (name_, independent_) {
this.avgOwnTimePerCall = 0;
this.avgRunTimePerCall = 0;
this.calls = 0;
this.maxOwnTimePerCall = 0;
this.maxRunTimePerCall = 0;
this.minOwnTimePerCall = 0;
this.minRunTimePerCall = 0;
this.name = name_;
this.ownTimeTotal = 0;
this.runTimeTotal = 0;
this.state = fnTimer.STATE_NONE;
this.independent = independent_;
var startTime_ = 0,
subTimerStart_ = 0,
subTime_ = 0,
stack_ = 0;
/*
* Start timing function execution.
*/
this.start = function () {
if (this.state == fnTimer.STATE_START ||
this.state == fnTimer.STATE_SUB) {
stack_++;
return;
}
startTime_ = (new Date ()).getTime();
this.state = fnTimer.STATE_START;
};
/*
* Stop timing function execution.
*/
this.stop = function () {
if (this.state == fnTimer.STATE_SUB) {
this.subTimerEnd();
}
if (this.state != fnTimer.STATE_START) {
return;
}
this.calls++;
if (stack_) {
stack_--;
return;
}
var runTime = (new Date ()).getTime() - startTime_;
var ownTime = runTime - subTime_;
subTime_ = 0;
this.runTimeTotal += runTime;
this.ownTimeTotal += ownTime;
this.avgRunTimePerCall = this.runTimeTotal / this.calls;
this.avgOwnTimePerCall = this.ownTimeTotal / this.calls;
if (runTime < this.minRunTimePerCall) {
this.minRunTimePerCall = runTime;
}
if (runTime > this.maxRunTimePerCall) {
this.maxRunTimePerCall = runTime;
}
if (ownTime < this.minOwnTimePerCall) {
this.minOwnTimePerCall = ownTime;
}
if (ownTime > this.maxOwnTimePerCall) {
this.maxOwnTimePerCall = ownTime;
}
this.state = fnTimer.STATE_STOP;
};
/*
* Start timing sub-function execution. The sub-function execution timer is
* used for calculating the ownTime (runTime - sub-function execution time).
*/
this.subTimerStart = function () {
if (this.independent || this.state != fnTimer.STATE_START) {
return;
}
subTimerStart_ = (new Date()).getTime();
this.state = fnTimer.STATE_SUB;
};
/*
* Stop timing sub-function execution.
*/
this.subTimerEnd = function () {
if (this.independent || this.state != fnTimer.STATE_SUB) {
return;
}
subTime_ += (new Date()).getTime() - subTimerStart_;
this.state = fnTimer.STATE_START;
};
};
fnTimer.STATE_NONE = 0;
fnTimer.STATE_START = 1;
fnTimer.STATE_SUB = 2;
fnTimer.STATE_STOP = 3;
/**
* Holds the timer objects.
*/
this.timers = {};
var activeTimer_ = null,
timersStack_ = [];
/**
* Start/create a function timer.
*
* @param {String} name The timer name.
* @param {Boolean} [independent=false] Tells if the timer should be
* independent or not. This means that this new function timer is not be
* considered affecting the execution time of existing function timers in the
* call stack.
*/
this.start = function (name, independent) {
var timer = this.timers[name];
if (!timer) {
timer = this.timers[name] = new fnTimer(name, independent);
}
if (!timer.independent && activeTimer_ != name) {
var activeTimer = activeTimer_ ? this.timers[activeTimer_] : null;
if (activeTimer && activeTimer.state == fnTimer.STATE_START) {
timersStack_.push(activeTimer_);
activeTimer.subTimerStart();
}
activeTimer_ = name;
}
timer.start();
};
/**
* Stop a function timer.
*/
this.stop = function (name) {
var timer = this.timers[name];
if (!timer) {
return;
}
timer.stop();
if (timer.independent || name != activeTimer_ ||
name == activeTimer_ && timer.state == fnTimer.STATE_START) {
return;
}
if (timersStack_.length > 0) {
activeTimer_ = timersStack_.pop();
var activeTimer = this.timers[activeTimer_];
activeTimer.subTimerEnd();
} else {
activeTimer_ = null;
}
};
/**
* Generate timers report data.
*
* @returns {Object} Holds all the information gathered by the timers.
*/
this.reportData = function () {
var name, timer, timerDetails,
data = {
avgCallsPerTimer: 0,
avgOwnTimePerCall: 0,
avgOwnTimePerTimer: 0,
avgRunTimePerCall: 0,
avgRunTimePerTimer: 0,
callsTotal: 0,
maxCallsPerTimer: 0,
maxCallsPerTimerName: '',
maxOwnTimePerCall: 0,
maxOwnTimePerCallName: '',
maxRunTimePerCall: 0,
maxRunTimePerCallName: '',
minCallsPerTimer: 0,
minCallsPerTimerName: '',
minOwnTimePerCall: 0,
minOwnTimePerCallName: '',
minRunTimePerCall: 0,
minRunTimePerCallName: '',
ownTimeTotal: 0,
runTimeTotal: 0,
timers: 0,
timerDetails: []
};
for (name in this.timers) {
timer = this.timers[name];
if (timer.state != fnTimer.STATE_STOP) {
continue;
}
timerDetails = {
name: name,
avgOwnTimePerCall: timer.avgOwnTimePerCall,
avgRunTimePerCall: timer.avgRunTimePerCall,
calls: timer.calls,
maxOwnTimePerCall: timer.maxOwnTimePerCall,
maxRunTimePerCall: timer.maxRunTimePerCall,
minOwnTimePerCall: timer.minOwnTimePerCall,
minRunTimePerCall: timer.minRunTimePerCall,
runTimeTotal: timer.runTimeTotal,
ownTimeTotal: timer.ownTimeTotal
};
data.timerDetails.push(timerDetails);
if (timer.calls > data.maxCallsPerTimer || !data.timers) {
data.maxCallsPerTimer = timer.calls;
data.maxCallsPerTimerName = name;
}
if (timer.maxOwnTimePerCall > data.maxOwnTimePerCall || !data.timers) {
data.maxOwnTimePerCall = timer.maxOwnTimePerCall;
data.maxOwnTimePerCallName = name;
}
if (timer.maxRunTimePerCall > data.maxRunTimePerCall || !data.timers) {
data.maxRunTimePerCall = timer.maxRunTimePerCall;
data.maxRunTimePerCallName = name;
}
if (timer.calls < data.minCallsPerTimer || !data.timers) {
data.minCallsPerTimer = timer.calls;
data.minCallsPerTimerName = name;
}
if (timer.minOwnTimePerCall < data.minOwnTimePerCall || !data.timers) {
data.minOwnTimePerCall = timer.minOwnTimePerCall;
data.minOwnTimePerCallName = name;
}
if (timer.minRunTimePerCall < data.minRunTimePerCall || !data.timers) {
data.minRunTimePerCall = timer.minRunTimePerCall;
data.minRunTimePerCallName = name;
}
data.runTimeTotal += timer.runTimeTotal;
data.ownTimeTotal += timer.ownTimeTotal;
data.callsTotal += timer.calls;
data.timers++;
}
if (data.timers == 0) {
return data;
}
data.avgCallsPerTimer = data.callsTotal / data.timers;
data.avgOwnTimePerCall = data.ownTimeTotal / data.callsTotal;
data.avgOwnTimePerTimer = data.ownTimeTotal / data.timers;
data.avgRunTimePerCall = data.runTimeTotal / data.callsTotal;
data.avgRunTimePerTimer = data.runTimeTotal / data.timers;
return data;
};
/**
* Generate a report in text format.
*
* @returns {String} All the information gathered by the timers, as text.
*/
this.reportText = function () {
var data = this.reportData(),
timer, result = '';
if (!data.timers) {
return '';
}
for (var i = 0; i < data.timers; i++) {
timer = data.timerDetails[i];
result += timer.name + ":\n" +
' Avg ownTime / call: ' + timer.avgOwnTimePerCall + " ms\n" +
' Avg runTime / call: ' + timer.avgRunTimePerCall + " ms\n" +
' Calls: ' + timer.calls + "\n"+
' Max ownTime / call: ' + timer.maxOwnTimePerCall + " ms\n" +
' Max runTime / call: ' + timer.maxRunTimePerCall + " ms\n" +
' Min ownTime / call: ' + timer.minOwnTimePerCall + " ms\n" +
' Min runTime / call: ' + timer.minRunTimePerCall + " ms\n" +
' runTime: ' + timer.runTimeTotal + " ms\n" +
' ownTime: ' + timer.ownTimeTotal + " ms\n\n";
}
result += "Overview info:\n" +
' Avg calls / timer: ' + data.avgCallsPerTimer + "\n" +
' Avg ownTime / call: ' + data.avgOwnTimePerCall + " ms\n" +
' Avg ownTime / timer: ' + data.avgOwnTimePerTimer + " ms\n" +
' Avg runTime / call: ' + data.avgRunTimePerCall + " ms\n" +
' Avg runTime / timer: ' + data.avgRunTimePerTimer + " ms\n" +
' Calls total: ' + data.callsTotal + "\n" +
' Max calls / timer: ' + data.maxCallsPerTimer + ' (' +
data.maxCallsPerTimerName + ")\n" +
' Max ownTime / call: ' + data.maxOwnTimePerCall + ' ms (' +
data.maxOwnTimePerCallName + ")\n" +
' Max runTime / call: ' + data.maxRunTimePerCall + ' ms (' +
data.maxRunTimePerCallName + ")\n" +
' Min calls / timer: ' + data.minCallsPerTimer + ' (' +
data.minCallsPerTimerName + ")\n" +
' Min ownTime / call: ' + data.minOwnTimePerCall + ' ms (' +
data.minOwnTimePerCallName + ")\n" +
' Min runTime / call: ' + data.minRunTimePerCall + ' ms (' +
data.minRunTimePerCallName + ")\n" +
' Accumulated ownTime: ' + data.ownTimeTotal + " ms\n" +
' Accumulated runTime: ' + data.runTimeTotal + " ms\n" +
' Timers: ' + data.timers;
return result;
};
/**
* Reset/clear all the timers.
*/
this.reset = function () {
this.timers = {};
activeTimer_ = null;
timersStack_ = [];
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,478 @@
/*
http://www.JSON.org/json2.js
2009-04-16
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the object holding the key.
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint evil: true */
/*global JSON */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,411 @@
<div xmlns="http://www.w3.org/1999/xhtml">
<!-- $Date: 2009-11-08 13:19:52 +0200 $ -->
<h1 class="paintweb_appTitle">PaintWeb</h1>
<!-- All elements with data-pwTabPanel become interactive tabbed panels which
will also have a class name like .paintweb_tabPanel_id and a generic class
name .paintweb_tabPanel. In this case the class names will be
.paintweb_tabPanel_main and .paintweb_tabPanel. -->
<div data-pwTabPanel="main" data-pwTabDefault="main">
<!-- All elements with data-pwTab will have a class name like
.paintweb_tabPanelId_tabID and a generic class name .paintweb_tab. In this
case the class names will be .paintweb_main_main and .paintweb_tab. -->
<div data-pwTab="main">
<!-- All elements with some #id will also have a class name like
.paintweb_id. In this case the class name will be .paintweb_tools. -->
<ul id="tools">
<!-- All elements with data-pwCommand will have a class name like
.paintweb_cmd_commandID and a generic class name .paintweb_command. In
this case the class names will be .paintweb_cmd_historyUndo and
.paintweb_command. -->
<!--FIXME: 페인트웹 기능 항목 추가/제거-->
<!--<li data-pwCommand="historyRedo">Redo</li>-->
<li data-pwCommand="historyUndo">Undo</li>
<li class="paintweb_toolSeparator">&#160;</li>
<li data-pwTool="selection">Selection</li>
<!--<li data-pwTool="hand">Hand</li>-->
<li data-pwTool="rectangle">Rectangle</li>
<li class="paintweb_toolSeparator">&#160;</li>
<!--<li data-pwCommand="selectionCut">Cut selection</li>-->
<!--<li data-pwCommand="selectionCopy">Copy selection</li>-->
<!--<li data-pwCommand="clipboardPaste">Clipboard paste</li>-->
<li data-pwCommand="selectionCrop">Crop selection</li>
<li data-pwCommand="selectionFill">Fill selection</li>
<!--<li data-pwTool="insertimg">Insert image</li>-->
<li class="paintweb_strokeFillStyles">
<p class="paintweb_opt_fillStyle">Fill
<span data-pwColorInput="fillStyle">&#160;</span>
</p>
<p class="paintweb_opt_strokeStyle">Stroke
<span data-pwColorInput="strokeStyle">&#160;</span>
</p>
</li>
<li class="paintweb_toolSeparator">&#160;</li>
<li data-pwCommand="imageRotate">Rotate image</li>
<li class="paintweb_buttonStyle" data-pwCommand="imageBright">brighten image</li>
<li class="paintweb_buttonStyle" data-pwCommand="imageDark">darken image</li>
<!-- All elements with data-pwTool will have a class name like
.paintweb_tool_toolID and a generic class name .paintweb_tool. In this
case the class names will be .paintweb_tool_insertimg and
.paintweb_tool. -->
<!--<li data-pwTool="text">Text</li>-->
<li class="paintweb_toolSeparator">&#160;</li>
<!--<li data-pwTool="cpicker">Color picker</li>
<li data-pwTool="cbucket">Color bucket</li>-->
<!--<li data-pwTool="ellipse">Ellipse</li>
<li data-pwTool="polygon">Polygon</li>
<li data-pwTool="line">Line</li>
<li data-pwTool="bcurve">Bézier curve</li>-->
<!--<li data-pwTool="pencil">Pencil</li>-->
<!--<li data-pwTool="eraser">Eraser</li>-->
<li data-pwCommand="imageClear">Clear image</li>
<li data-pwCommand="imageSave">Save image</li>
</ul>
</div>
<!-- Note that all tool/command elements will have an anchor elements as the
only child. The locale string will be added as child of the anchor. Having
the anchors allows users to tab through the tools and commands - keyboard
accessibility. -->
<!-- .paintweb_main_line -->
<!-- FIXME : line 툴 사용시
<div data-pwTab="line" data-pwTabHide="true">
&lt;!&ndash; Elements with data-pwConfig will have class names like
paintweb_cfg_configVariable. In this case the class name will be
.paintweb_cfg_line_lineWidth. &ndash;&gt;
<p class="paintweb_opt_lineWidth"><label>Line width <input
data-pwConfig="line.lineWidth" type="number" min="1" value="1"
/></label></p>
&lt;!&ndash; .paintweb_cfg_line_miterLimit (dot becomes underscore) &ndash;&gt;
<p class="paintweb_opt_miterLimit"><label>Miter limit <input
data-pwConfig="line.miterLimit" type="number" min="1" value="10"
/></label></p>
&lt;!&ndash; .paintweb_cfg_line_lineCap &ndash;&gt;
<div data-pwConfig="line.lineCap">
<p>Line cap</p>
&lt;!&ndash; Elements with data-pwConfigValue will have the class name based on
the configuration property name and value. In this case the class name
will be .paintweb_lineCap_butt &ndash;&gt;
<div data-pwConfigValue="butt">Butt</div>
&lt;!&ndash; .paintweb_lineCap_square &ndash;&gt;
<div data-pwConfigValue="square">Square</div>
&lt;!&ndash; .paintweb_lineCap_round &ndash;&gt;
<div data-pwConfigValue="round">Round</div>
</div>
&lt;!&ndash; .paintweb_cfg_line_lineJoin &ndash;&gt;
<div data-pwConfig="line.lineJoin">
<p>Line join</p>
&lt;!&ndash; .paintweb_lineJoin_miter &ndash;&gt;
<div data-pwConfigValue="miter">Miter</div>
&lt;!&ndash; .paintweb_lineJoin_round &ndash;&gt;
<div data-pwConfigValue="round">Round</div>
&lt;!&ndash; .paintweb_lineJoin_bevel &ndash;&gt;
<div data-pwConfigValue="bevel">Bevel</div>
</div>
&lt;!&ndash; .paintweb_cfg_shapeType &ndash;&gt;
<div data-pwConfig="shapeType">
<p>Shape type</p>
&lt;!&ndash; .paintweb_shapeType_both &ndash;&gt;
<div data-pwConfigValue="both">Both</div>
&lt;!&ndash; .paintweb_shapeType_fill &ndash;&gt;
<div data-pwConfigValue="fill">Fill</div>
&lt;!&ndash; .paintweb_shapeType_stroke &ndash;&gt;
<div data-pwConfigValue="stroke">Stroke</div>
</div>
&lt;!&ndash; erm *cough* elements with data-pwConfigValue will be considered icons
(class name .paintweb_icon is added). They will also have an anchor
element appended. &ndash;&gt;
</div>
-->
<!-- .paintweb_main_selection -->
<!--<div data-pwTab="selection" data-pwTabHide="true">
<p data-pwId="selTab_selectionCut">Cut selection</p>
<p data-pwId="selTab_selectionCopy">Copy selection</p>
<p data-pwId="selTab_clipboardPaste">Clipboard paste</p>
<p data-pwCommand="selectionCrop">Crop selection</p>
<p data-pwCommand="selectionDelete">Delete selection</p>
<p data-pwCommand="selectionFill">Fill selection</p>
<p class="paintweb_opt_selectionTransparent">
<label><input data-pwConfig="selection.transparent" type="checkbox"
value="1" checked="checked" /> Transparent background</label>
</p>
<p class="paintweb_opt_selectionTransform">
<label><input data-pwConfig="selection.transform" type="checkbox"
value="1" /> Transformation mode</label>
</p>
</div>-->
<!-- .paintweb_main_text -->
<!-- FIXME : text 툴 사용시
<div data-pwTab="text" data-pwTabHide="true" class="paintweb_tab02">
<p class="paintweb_opt_fontFamily">
<label for="fontFamily">Font family:</label>
<select id="fontFamily" data-pwConfig="text.fontFamily"></select>
</p>
<p class="paintweb_opt_fontSize">
<label for="fontSize">Font size:</label>
<select id="fontSize" data-pwConfig="text.fontSize" style="width:58px"></select>
</p>
<div data-pwConfigButton="text.bold" style="padding-top: 4px;">Bold</div>
<div data-pwConfigButton="text.italic">Italic</div>
<div data-pwConfig="text.textAlign">
<p>Text alignment</p>
<div data-pwConfigButton="text.left">Left</div>
<div data-pwConfigButton="text.center">Center</div>
<div data-pwConfigButton="text.right">Right</div>
</div>
</div>
-->
<!-- .paintweb_main_shadow -->
<!-- FIXME : shadow 툴 사용시
<div data-pwTab="shadow">
<p class="paintweb_opt_shadowEnable"><label><input
data-pwConfig="shadow.enable" type="checkbox" value="1" /> Draw
shadows</label></p>
<p class="paintweb_opt_shadowColor">Color <span
data-pwColorInput="shadow.shadowColor">&#160;</span>
</p>
<p class="paintweb_opt_shadowOffsetX">
<label>Offset X
<input data-pwConfig="shadow.shadowOffsetX" type="number" value="5" />
</label>
</p>
<p class="paintweb_opt_shadowOffsetY">
<label>Offset Y
<input data-pwConfig="shadow.shadowOffsetY" type="number" value="5" />
</label>
</p>
<p class="paintweb_opt_shadowBlur">
<label>Blur
<input data-pwConfig="shadow.shadowBlur" type="number" value="5"
min="0" />
</label>
</p>
</div>
-->
<!-- <p data-pwCommand="about">About</p>-->
</div> <!-- .paintweb_tabPanel_main -->
<div id="viewport">
<div id="canvasContainer">
</div>
<div id="canvasResizer">Resize the image Canvas.</div>
</div> <!-- .paintweb_viewport -->
<div class="paintweb_statusbar">
<p id="imageSize">WxH</p>
<p id="statusZoom" title="Zoom image">
<label>Zoom: <input id="imageZoom" type="number" min="20" max="400" value="100" step="10" /></label>
</p>
<p id="statusMessage">Status</p>
<p id="viewportResizer">Resize the image viewport.</p>
</div> <!-- .paintweb_statusbar -->
<!-- Elements with data-pwFloatingPanel become interactive GUI components.
These will also have two class names: .paintweb_floatingPanel and
.paintweb_floatingPanel_id. In this case, the id is "colormixer". If you want
to automatically hide the panel, simply use data-pwPanelHide="true".
Additionally, you can make the panel resizable if you add
data-pwPanelResizable="true". -->
<div data-pwFloatingPanel="colormixer" data-pwPanelHide="true">
<!-- Make sure you have the panel title in the h1 element. This will have
the .paintweb_floatingPanel_title class name. -->
<h1>Color mixer</h1>
<!-- Wrap your content in a single div. This element will have the
.paintweb_floatingPanel_content class name. -->
<div>
<ol class="paintweb_colormixer_preview">
<li id="colormixer_colorActive"><span>&#160;</span> Active</li>
<li id="colormixer_colorOld"><span>&#160;</span> Old</li>
</ol>
<ol class="paintweb_colormixer_actions">
<li id="colormixer_btn_accept">Close</li>
<li id="colormixer_btn_cancel">Cancel</li>
<li id="colormixer_btn_saveColor">Save color</li>
<li id="colormixer_btn_pickColor">Pick color</li>
</ol>
<div data-pwTabPanel="colormixer_selector" data-pwTabDefault="mixer">
<div data-pwTab="mixer">
<canvas id="colormixer_canvas" width="200" height="195">Your browser
does not support Canvas.</canvas>
<div id="colormixer_controls">
<span id="colormixer_chartDot"></span>
<span id="colormixer_slider"></span>
</div>
</div>
<div data-pwTab="cpalettes">
<select id="colormixer_cpaletteInput"></select>
<div id="colormixer_cpaletteOutput"></div>
</div>
</div>
<ol class="paintweb_colormixer_hexalpha">
<li><label>HEX
<input id="ckey_hex" value="#RRGGBB" type="text" maxlength="7"
pattern="#[a-f0-9]{6}" /></label>
</li>
<li><label>Alpha
<input id="ckey_alpha" value="100" type="number" min="0" max="100"
step="1" /></label>
</li>
</ol>
<form data-pwTabPanel="colormixer_inputs" data-pwTabDefault="rgb">
<ol data-pwTab="rgb">
<li>
<input name="ckey" value="red" type="radio" />
<label>Red
<input name="ckey_red" value="0" type="number" min="0" max="255"
step="1" /></label>
</li>
<li>
<input name="ckey" value="green" type="radio" />
<label>Green
<input name="ckey_green" value="0" type="number" min="0" max="255"
step="1" /></label>
</li>
<li>
<input name="ckey" value="blue" type="radio" />
<label>Blue
<input name="ckey_blue" value="0" type="number" min="0" max="255"
step="1" /></label>
</li>
</ol>
<ol data-pwTab="hsv">
<li>
<input name="ckey" value="hue" type="radio" />
<label>Hue
<input name="ckey_hue" value="0" type="number" min="0" max="360"
step="1" /></label>
</li>
<li>
<input name="ckey" value="sat" type="radio" />
<label>Saturation
<input name="ckey_sat" value="0" type="number" min="0" max="255"
step="1" /></label>
</li>
<li>
<input name="ckey" value="val" type="radio" />
<label>Value
<input name="ckey_val" value="0" type="number" min="0" max="255"
step="1" /></label>
</li>
</ol>
<ol data-pwTab="lab">
<li>
<input name="ckey" value="cie_l" type="radio" />
<label>Lightness
<input name="ckey_cie_l" value="0" type="number" min="0"
max="100" step="1" /></label>
</li>
<li>
<input name="ckey" value="cie_a" type="radio" />
<label>a*
<input name="ckey_cie_a" value="0" type="number" min="-85"
max="94" step="1" /></label>
</li>
<li>
<input name="ckey" value="cie_b" type="radio" />
<label>b*
<input name="ckey_cie_b" value="0" type="number" min="-109"
max="95" step="1" /></label>
</li>
</ol>
<ol data-pwTab="cmyk">
<li>
<label>Cyan
<input name="ckey_cyan" value="0" type="number" min="0" max="100"
step="1" /></label>
</li>
<li>
<label>Magenta
<input name="ckey_magenta" value="0" type="number" min="0"
max="100" step="1" /></label>
</li>
<li>
<label>Yellow
<input name="ckey_yellow" value="0" type="number" min="0" max="100"
step="1" /></label>
</li>
<li>
<label>Key (Black)
<input name="ckey_black" value="0" type="number" min="0" max="100"
step="1" /></label>
</li>
</ol>
</form> <!-- .paintweb_colormixer_inputs -->
</div> <!-- content wrap -->
</div> <!-- the colormixer floating panel -->
<div data-pwFloatingPanel="about" data-pwPanelHide="true">
<h1>About</h1>
<div>
<ul>
<li id="version"><strong>Version:</strong> </li>
<li><strong>Authors:</strong> <a href="http://www.robodesign.ro">Marius and Mihai Şucan (ROBO Design)</a></li>
<li><strong>Modifier:</strong><a href="https://github.com/gujc71/imageEditor">gujc</a></li>
<li><strong>Project site:</strong> <a href="http://code.google.com/p/paintweb">code.google.com/p/paintweb</a></li>
<li><strong>Modified site:</strong> <a href="https://github.com/gujc71/imageEditor">github.com/gujc71/imageEditor</a></li>
<li><strong>Code license:</strong> <a
href="https://opensource.org/licenses/BSD-3-Clause" title="BSD License, version 3">New BSD License</a></li>
</ul>
<p>For user and developer documentation please check out the <a href="http://code.google.com/p/paintweb">project site</a>.</p>
</div>
</div>
<!-- vim:set spell spl=en fo=tcroqwanl1 tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix: -->
</div>

@ -0,0 +1,268 @@
{
// $Date: 2009-11-16 18:13:48 +0200 $
"errorInitBufferCanvas": "Error: adding the new buffer canvas element failed.",
"errorInitContext": "Error while initializing the canvas context.",
"errorElementNotFound": "Error: the following element was not found: {id}.",
"noComputedStyle": "Error: window.getComputedStyle is not available.",
"noXMLHttpRequest": "Error: window.XMLHttpRequest is not available.",
"errorInitCanvas": "Error: Canvas initialization failed.",
"noCanvasSupport": "Error: Your browser does not support Canvas.",
"failedConfigLoad": "Error: Failed loading the configuration.",
"failedLangLoad": "Error: Failed loading the language file.",
"failedMarkupLoad": "Error: Failed loading the interface markup file.",
"errorInitCommands": "Error: failed to initialize the PaintWeb commands!",
"noToolConfigured": "Error: you have no drawing tool configured to load!",
"imageLoadDifferentHost": "Warning: the configured image cannot be loaded because it is from a different domain.",
"toolRegisterFailed": "Error: failed to register tool '{id}'!",
"extensionRegisterFailed": "Error: failed to register extension '{id}'!",
"errorToolActivate": "Error: the tool you want could not be properly activated!",
"errorInitGUI": "Error: the interface failed to initialize!",
"failedSelectionCopy": "Error: failed to copy the selected pixels into memory.",
"noMainTabPanel": "Error: the interface layout has no tabbed panel with ID = main.",
"guiMarkupImportFailed": "Error: the interface markup code could not be imported into the main document.",
"guiMarkupParseFailed": "Error: the interface markup code could not be properly parsed.",
"missingViewport": "Error: the interface markup does not have the image viewport element.",
"missingViewportResizer": "Error: the interface markup does not have the image viewport resize handle.",
"missingCanvasResizer": "Error: the interface markup does not have the Canvas resize handle.",
"missingCanvasContainer": "Error: the interface markup does not have the Canvas container.",
"errorCpickerUnsupported": "Error: your browser does not implement the get/putImageData methods! The color picker tool cannot be used.",
"errorCbucketUnsupported": "Error: your browser does not implement the get/putImageData methods! The color bucket tool cannot be used.",
"errorClipboardUnsupported": "Error: your browser does not support get/putImageData! Clipboard operations like cut/copy/paste cannot be used.",
"errorTextUnsupported": "Error: your browser does not implement the Canvas Text API! The text tool cannot be used.",
"errorInsertimg": "The image could not be inserted. Maybe the address does not point to an image.",
"errorInsertimgHost": "The URL you provided points to a different host. The image cannot be added for security reasons.",
"errorInsertimgNotLoaded": "The image did not load yet, or the URL you provided does not point to an image.",
"promptInsertimg": "Type the address of the image you want to insert:",
"promptImageDimensions": "Please input the new image dimensions you want.",
"promptTextFont": "Type the name of the font you want:",
"errorImageSave": "The image cannot be saved!",
"guiCanvasResizer": "Resize the image canvas.",
"guiViewportResizer": "Resize the image viewport.",
"imageZoomTitle": "Zoom image (Use Up/Down Arrow key)",
"imageZoomLabel": "Zoom:",
"tabs": {
"main": {
"bcurve": "Bézier curve",
"ellipse": "Ellipse",
"eraser": "Eraser",
"line": "Line",
"main": "Main",
"pencil": "Pencil",
"polygon": "Polygon",
"rectangle": "Rectangle",
"selection": "Selection",
"shadow": "Shadow",
"text": "Text",
"textBorder": "Border"
},
"colormixer_inputs": {
"rgb": "RGB",
"rgbTitle": "sRGB: Standard Red, Green and Blue",
"hsv": "HSV",
"hsvTitle": "Hue, Saturation and Value",
"lab": "Lab",
"labTitle": "CIE Lab: Standard observer 2° D65",
"cmyk": "CMYK",
"cmykTitle": "Cyan, Magenta, Yellow and Key (Black)"
},
"colormixer_selector": {
"mixer": "Mixer",
"mixerTitle": "Color space visualisation",
"cpalettes": "Palettes",
"cpalettesTitle": "Predefined color palettes"
}
},
"floatingPanelMinimize": "Minimize",
"floatingPanelRestore": "Restore",
"floatingPanelClose": "Close",
"floatingPanels": {
"about": "About PaintWeb",
"colormixer": "Color mixer"
},
"tools": {
"cbucket": "Color bucket",
"cpicker": "Color picker",
"bcurve": "Bézier curve",
"hand": "Viewport drag",
"ellipse": "Ellipse",
"eraser": "Eraser",
"insertimg": "Insert image",
"line": "Line",
"pencil": "Pencil",
"polygon": "Polygon",
"rectangle": "Rectangle",
"selection": "Rectangle selection",
"text": "Text",
"textUnsupported": "The text tool is not supported by your browser."
},
"commands": {
"about": "About PaintWeb",
"clipboardPaste": "Paste clipboard",
"historyRedo": "Redo",
"historyUndo": "Undo",
"imageClear": "Clear image",
"imageSave": "Save image",
"imageRotate": "Rotate image",
"imageBright": "brighten image",
"imageDark": "darken image",
"selectionCopy": "Copy selection",
"selectionCrop": "Crop selection",
"selectionCut": "Cut selection",
"selectionDelete": "Delete selection",
"selectionFill": "Fill the selection"
},
"inputs": {
"line": {
"lineCap": "Line cap",
"lineCap_butt": "Butt",
"lineCap_round": "Round",
"lineCap_square": "Square",
"lineJoin": "Line join",
"lineJoin_bevel": "Bevel",
"lineJoin_miter": "Miter",
"lineJoin_round": "Round",
"lineWidth": "Line width:",
"miterLimit": "Miter limit:"
},
"shadow": {
"enable": "Enable shadows",
"enableTitle": "If checked, a shadow will render after each drawing operation you do.",
"shadowBlur": "Blur:",
"shadowOffsetX": "Distance X:",
"shadowOffsetY": "Distance Y:",
"shadowColor": "Color: ",
"shadowColorTitle": "Shadow color"
},
"selection": {
"transform": "Image manipulation",
"transformTitle": "If checked, the selected pixels will also be dragged/resized when you make changes to the selection. If unchecked, only the selection marquee will be dragged/resized - pixels will remain unaffected by any such changes.",
"transparent": "Transparent selection",
"transparentTitle": "If checked, the background will remain transparent. If unchecked, the background will be filled with the current fill color."
},
"text": {
"bold": "Bold",
"italic": "Italic",
"fontFamily": "Font family:",
"fontFamily_add": "Another font...",
"fontSize": "Font size:",
"textAlign": "Text alignment",
"left": "Left",
"center": "Center",
"right": "Right",
"textString_value": "Hello world!"
},
"shapeType": "Shape type",
"shapeType_both": "Both",
"shapeType_fill": "Fill",
"shapeType_stroke": "Stroke",
"pencilSize": "Pencil size:",
"eraserSize": "Eraser size:",
"borderWidth": "Border width:",
"fillStyle": "Fill ",
"fillStyleTitle": "Fill color",
"strokeStyle": "Stroke ",
"strokeStyleTitle": "Stroke color",
"colorInputAnchorContent": "Click to pick color"
},
"colormixer": {
"failedColorPaletteLoad": "Error: failed to load the color palette.",
"colorPalettes": {
"_saved": "Saved colors",
"anpa": "ANPA",
"dic": "DIC Color Guide",
"macos": "Mac OS",
"pantone-solid-coated": "PANTONE solid coated",
"toyo94": "TOYO 94 color finder",
"trumatch": "TRUMATCH colors",
"web": "Web safe",
"windows": "Windows"
},
"inputs": {
"hex": "Hex",
"alpha": "Alpha",
"hsv_hue": "Hue",
"hsv_sat": "Saturation",
"hsv_val": "Value",
"rgb_red": "Red",
"rgb_green": "Green",
"rgb_blue": "Blue",
"lab_cie_l": "Lightness",
"lab_cie_a": "a*",
"lab_cie_b": "b*",
"cmyk_cyan": "Cyan",
"cmyk_magenta": "Magenta",
"cmyk_yellow": "Yellow",
"cmyk_black": "Key / Black"
},
"buttons": {
"accept": "Accept",
"cancel": "Cancel",
"saveColor": "Save color",
"pickColor": "Pick color"
}
},
"status": {
"cbucketActive": "Click to start flood filling with the current fill color. Right click to use the stroke color for filling.",
"cpickerNormal": "Click to change the fill color, or Shift+Click to change the stroke color.",
"cpicker_fillStyle": "Click to pick the fill color.",
"cpicker_strokeStyle": "Click to pick the stroke color.",
"cpicker_shadow_shadowColor": "Click to pick the shadow color.",
"bcurveActive": "Click to start drawing the curve. You need four points: start, end and two control points.",
"bcurveControlPoint1": "Click to draw the first control point.",
"bcurveControlPoint2": "Click to draw the second control point. This will also end the drawing operation.",
"bcurveSnapping": "Hold the Shift key down for vertical/horizontal snapping.",
"handActive": "Click and drag the image to scroll.",
"ellipseActive": "Click and drag to draw an ellipse.",
"ellipseMousedown": "Hold the Shift key down to draw a circle.",
"eraserActive": "Click and drag to erase.",
"insertimgActive": "Waiting for the image to load...",
"insertimgLoaded": "Pick where you want to place the image. Click and drag to resize the image.",
"insertimgResize": "Hold the Shift key down to preserve the aspect ratio.",
"lineActive": "Click anywhere to start drawing a line.",
"lineMousedown": "Hold the Shift key down for vertical/horizontal snapping.",
"pencilActive": "Click and drag to draw.",
"polygonActive": "Click anywhere to start drawing a polygon.",
"polygonAddPoint": "Click to add another point to the polygon.",
"polygonEnd": "To end drawing the polygon simply click in the same place as the last point.",
"polygonMousedown": "Hold the Shift key down for vertical/horizontal snapping.",
"rectangleActive": "Click and drag to draw a rectangle.",
"rectangleMousedown": "Hold the Shift key down to draw a square.",
"selectionActive": "Click and drag to draw a selection.",
"selectionAvailable": "Drag or resize the selection. Hold the Control key down to toggle the transformation mode.",
"selectionDrag": "Hold the Shift key down for vertical/horizontal snapping.",
"selectionDraw": "Hold the Shift key down to draw a square selection.",
"selectionResize": "Hold the Shift key down to preserve the aspect ratio.",
"textActive": "Pick where you want to place the text. Make sure you adjust the properties as desired.",
"guiCanvasResizerActive": "Move the mouse to resize the image canvas."
},
// Moodle-related language strings
"moodle": {
"xhrRequestFailed": "The image save request failed.",
"jsonParseFailed": "Parsing the JSON result from the server failed!",
"imageSaveFailed": "The image save operation failed.",
"urlMismatch": "Image address mismatch!\nThe current image is {url}.\nThe server replied a successful save for {urlServer}.",
"errorSubmitUnsaved": "This image is not saved!"
},
"moodleServer": {
"permissionDenied": "Permission denied.",
"saveEmptyDataUrl": "Your request has no data URL.",
"proxyNotFound": "Could not find the PaintWeb image file proxy script.",
"malformedDataUrl": "The data URL is malformed.",
"failedMkdir": "Failed to create the PaintWeb images folder inside the Moodle data folder.",
"saveFailed": "Saving the image failed.",
"backingupImages": "Backing-up images saved with PaintWeb...",
"backupFailed": "An error occurred while copying images saved by PaintWeb."
}
// vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix ft=javascript:
}

@ -0,0 +1,268 @@
{
// $Date: 2009-11-16 18:13:48 +0200 $
"errorInitBufferCanvas": "Error: adding the new buffer canvas element failed.",
"errorInitContext": "Error while initializing the canvas context.",
"errorElementNotFound": "Error: the following element was not found: {id}.",
"noComputedStyle": "Error: window.getComputedStyle is not available.",
"noXMLHttpRequest": "Error: window.XMLHttpRequest is not available.",
"errorInitCanvas": "Error: Canvas initialization failed.",
"noCanvasSupport": "Error: Your browser does not support Canvas.",
"failedConfigLoad": "Error: Failed loading the configuration.",
"failedLangLoad": "Error: Failed loading the language file.",
"failedMarkupLoad": "Error: Failed loading the interface markup file.",
"errorInitCommands": "Error: failed to initialize the PaintWeb commands!",
"noToolConfigured": "Error: you have no drawing tool configured to load!",
"imageLoadDifferentHost": "Warning: the configured image cannot be loaded because it is from a different domain.",
"toolRegisterFailed": "Error: failed to register tool '{id}'!",
"extensionRegisterFailed": "Error: failed to register extension '{id}'!",
"errorToolActivate": "Error: the tool you want could not be properly activated!",
"errorInitGUI": "Error: the interface failed to initialize!",
"failedSelectionCopy": "Error: failed to copy the selected pixels into memory.",
"noMainTabPanel": "Error: the interface layout has no tabbed panel with ID = main.",
"guiMarkupImportFailed": "Error: the interface markup code could not be imported into the main document.",
"guiMarkupParseFailed": "Error: the interface markup code could not be properly parsed.",
"missingViewport": "Error: the interface markup does not have the image viewport element.",
"missingViewportResizer": "Error: the interface markup does not have the image viewport resize handle.",
"missingCanvasResizer": "Error: the interface markup does not have the Canvas resize handle.",
"missingCanvasContainer": "Error: the interface markup does not have the Canvas container.",
"errorCpickerUnsupported": "Error: your browser does not implement the get/putImageData methods! The color picker tool cannot be used.",
"errorCbucketUnsupported": "Error: your browser does not implement the get/putImageData methods! The color bucket tool cannot be used.",
"errorClipboardUnsupported": "Error: your browser does not support get/putImageData! Clipboard operations like cut/copy/paste cannot be used.",
"errorTextUnsupported": "Error: your browser does not implement the Canvas Text API! The text tool cannot be used.",
"errorInsertimg": "The image could not be inserted. Maybe the address does not point to an image.",
"errorInsertimgHost": "The URL you provided points to a different host. The image cannot be added for security reasons.",
"errorInsertimgNotLoaded": "The image did not load yet, or the URL you provided does not point to an image.",
"promptInsertimg": "Type the address of the image you want to insert:",
"promptImageDimensions": "Please input the new image dimensions you want.",
"promptTextFont": "Type the name of the font you want:",
"errorImageSave": "The image cannot be saved!",
"guiCanvasResizer": "이미지 캔버스의 크기 조정.",
"guiViewportResizer": "Resize the image viewport.",
"imageZoomTitle": "이미지 확대/축소(화살표키를 이용하세요)",
"imageZoomLabel": "Zoom:",
"tabs": {
"main": {
"bcurve": "베지어 곡선",
"ellipse": "타원",
"eraser": "지우개",
"line": "라인",
"main": "메인",
"pencil": "연필",
"polygon": "다각형",
"rectangle": "사각형",
"selection": "선택",
"shadow": "그림자",
"text": "텍스트",
"textBorder": "외곽선"
},
"colormixer_inputs": {
"rgb": "RGB",
"rgbTitle": "sRGB: Standard Red, Green and Blue",
"hsv": "HSV",
"hsvTitle": "Hue, Saturation and Value",
"lab": "Lab",
"labTitle": "CIE Lab: Standard observer 2° D65",
"cmyk": "CMYK",
"cmykTitle": "Cyan, Magenta, Yellow and Key (Black)"
},
"colormixer_selector": {
"mixer": "믹서",
"mixerTitle": "색 영역 시각화",
"cpalettes": "팔레트",
"cpalettesTitle": "미리 정의된 색상 팔레트"
}
},
"floatingPanelMinimize": "Minimize",
"floatingPanelRestore": "Restore",
"floatingPanelClose": "Close",
"floatingPanels": {
"about": "About PaintWeb",
"colormixer": "색상"
},
"tools": {
"cbucket": "채우기",
"cpicker": "색 선택",
"bcurve": "베지어 곡선",
"hand": "작업영역 움직이기",
"ellipse": "타원",
"eraser": "지우개",
"insertimg": "이미지 추가",
"line": "선 그리기",
"pencil": "연필",
"polygon": "다각형",
"rectangle": "사각형",
"selection": "영역 선택",
"text": "문자열",
"textUnsupported": "텍스트 도구는 브라우저에서 지원되지 않습니다."
},
"commands": {
"about": "About PaintWeb",
"clipboardPaste": "붙여넣기",
"historyRedo": "다시 실행",
"historyUndo": "실행취소",
"imageClear": "Clear image",
"imageSave": "이미지 저장",
"imageRotate": "이미지 회전",
"imageBright": "밝게",
"imageDark": "어둡게",
"selectionCopy": "선택영역 복사",
"selectionCrop": "선택영역만 자르기",
"selectionCut": "선택영역 잘라내기",
"selectionDelete": "선택영역 삭제",
"selectionFill": "선택영역 채우기"
},
"inputs": {
"line": {
"lineCap": "선끝",
"lineCap_butt": "약간둥굴게",
"lineCap_round": "둥굴게",
"lineCap_square": "직각",
"lineJoin": "Line join",
"lineJoin_bevel": "Bevel",
"lineJoin_miter": "Miter",
"lineJoin_round": "Round",
"lineWidth": "선 굵기:",
"miterLimit": "Miter limit:"
},
"shadow": {
"enable": "그림자 활성화",
"enableTitle": "선택하면 그리기 작업 후에 그림자가 렌더링됩니다.",
"shadowBlur": "Blur:",
"shadowOffsetX": "Distance X:",
"shadowOffsetY": "Distance Y:",
"shadowColor": "Color: ",
"shadowColorTitle": "그림자 색상"
},
"selection": {
"transform": "이미지 조작",
"transformTitle": "선택하면 선택 항목을 변경할 때 선택한 픽셀도 드래그/크기 조정됩니다. 선택하지 않으면 선택 마키만 드래그/크기 조정됩니다. 픽셀은 이러한 변경 사항에 영향을 받지 않습니다.",
"transparent": "투명한 선택",
"transparentTitle": "선택하면 배경이 투명하게 유지됩니다. 선택하지 않으면 배경이 현재 채우기 색상으로 채워집니다."
},
"text": {
"bold": "굵게",
"italic": "기울임꼴",
"fontFamily": "Font family:",
"fontFamily_add": "Another font...",
"fontSize": "Font size:",
"textAlign": "Text alignment",
"left": "Left",
"center": "Center",
"right": "Right",
"textString_value": "문자를 입력하세요"
},
"shapeType": "도형종류",
"shapeType_both": "채우기와 외곽선",
"shapeType_fill": "채우기만",
"shapeType_stroke": "외곽선만",
"pencilSize": "연필 굵기:",
"eraserSize": "지우개 크기:",
"borderWidth": "외곽선 너비:",
"fillStyle": " ",
"fillStyleTitle": "배경색",
"strokeStyle": " ",
"strokeStyleTitle": "전경색",
"colorInputAnchorContent": "클릭하여 색상 선택"
},
"colormixer": {
"failedColorPaletteLoad": "Error: 색상 팔레트를 로드하지 못했습니다.",
"colorPalettes": {
"_saved": "Saved colors",
"anpa": "ANPA",
"dic": "DIC Color Guide",
"macos": "Mac OS",
"pantone-solid-coated": "PANTONE solid coated",
"toyo94": "TOYO 94 color finder",
"trumatch": "TRUMATCH colors",
"web": "Web safe",
"windows": "Windows"
},
"inputs": {
"hex": "Hex",
"alpha": "Alpha",
"hsv_hue": "Hue",
"hsv_sat": "Saturation",
"hsv_val": "Value",
"rgb_red": "Red",
"rgb_green": "Green",
"rgb_blue": "Blue",
"lab_cie_l": "Lightness",
"lab_cie_a": "a*",
"lab_cie_b": "b*",
"cmyk_cyan": "Cyan",
"cmyk_magenta": "Magenta",
"cmyk_yellow": "Yellow",
"cmyk_black": "Key / Black"
},
"buttons": {
"accept": "선택",
"cancel": "취소",
"saveColor": "Save color",
"pickColor": "Pick color"
}
},
"status": {
"cbucketActive": "현재 채우기 색상으로 플러드 채우기를 시작하려면 클릭합니다. 채우기에 획 색상을 사용하려면 마우스 오른쪽 버튼을 클릭합니다.",
"cpickerNormal": "Click to change the fill color, or Shift+Click to change the stroke color.",
"cpicker_fillStyle": "채우기 색상을 선택하려면 클릭합니다.",
"cpicker_strokeStyle": "색상을 선택하려면 클릭합니다.",
"cpicker_shadow_shadowColor": "그림자 색상 선택[클릭]합니다.",
"bcurveActive": "곡선 그리기를 시작하려면 클릭합니다. [네 개의 점 필요: 시작점, 끝점, 두 개의 제어점]",
"bcurveControlPoint1": "클릭하여 첫 번째 제어점을 그립니다.",
"bcurveControlPoint2": "클릭하여 두 번째 제어점을 그립니다. 이렇게 하면 그리기 작업도 종료됩니다.",
"bcurveSnapping": "수직/수평 스냅을 위해 Shift 키를 누르고 있습니다.",
"handActive": "이미지를 클릭하고 드래그하여 스크롤합니다.",
"ellipseActive": "클릭하고 드래그하여 타원을 그립니다.",
"ellipseMousedown": "Shift 키를 누른 상태에서 원을 그립니다.",
"eraserActive": "클릭하고 드래그하여 지웁니다.",
"insertimgActive": "이미지가 로드되기를 기다리는 중...",
"insertimgLoaded": "이미지를 배치할 위치를 선택합니다. 클릭하고 드래그하여 이미지 크기를 조정합니다.",
"insertimgResize": "종횡비를 유지하려면 Shift 키를 누르고 계십시오.",
"lineActive": "아무 곳이나 클릭하여 선 그리기를 시작합니다..",
"lineMousedown": "수직/수평 스냅을 위해 Shift 키를 누르고 있습니다.",
"pencilActive": "클릭하고 드래그하여 그립니다.",
"polygonActive": "Click anywhere to start drawing a polygon.",
"polygonAddPoint": "Click to add another point to the polygon.",
"polygonEnd": "To end drawing the polygon simply click in the same place as the last point.",
"polygonMousedown": "Hold the Shift key down for vertical/horizontal snapping.",
"rectangleActive": "Click and drag to draw a rectangle.",
"rectangleMousedown": "Hold the Shift key down to draw a square.",
"selectionActive": "Click and drag to draw a selection.",
"selectionAvailable": "Drag or resize the selection. Hold the Control key down to toggle the transformation mode.",
"selectionDrag": "Hold the Shift key down for vertical/horizontal snapping.",
"selectionDraw": "Hold the Shift key down to draw a square selection.",
"selectionResize": "Hold the Shift key down to preserve the aspect ratio.",
"textActive": "Pick where you want to place the text. Make sure you adjust the properties as desired.",
"guiCanvasResizerActive": "Move the mouse to resize the image canvas."
},
// Moodle-related language strings
"moodle": {
"xhrRequestFailed": "The image save request failed.",
"jsonParseFailed": "Parsing the JSON result from the server failed!",
"imageSaveFailed": "The image save operation failed.",
"urlMismatch": "Image address mismatch!\nThe current image is {url}.\nThe server replied a successful save for {urlServer}.",
"errorSubmitUnsaved": "This image is not saved!"
},
"moodleServer": {
"permissionDenied": "Permission denied.",
"saveEmptyDataUrl": "Your request has no data URL.",
"proxyNotFound": "Could not find the PaintWeb image file proxy script.",
"malformedDataUrl": "The data URL is malformed.",
"failedMkdir": "Failed to create the PaintWeb images folder inside the Moodle data folder.",
"saveFailed": "Saving the image failed.",
"backingupImages": "Backing-up images saved with PaintWeb...",
"backupFailed": "An error occurred while copying images saved by PaintWeb."
}
// vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix ft=javascript:
}

@ -0,0 +1,644 @@
{
// $Date: 2009-11-08 19:54:46 +0200 $
"languages": {
"ko": { "title": "Korean" }
},
"lang": "ko",
"langFolder": "lang",
/*
The graphical user interface you want to use.
@type String
@default "default"
*/
"gui": "default",
/**
* The folder contains all the interfaces.
*
* @type String
* @default "interfaces"
*/
"interfacesFolder": "interfaces",
/**
* The interface markup file. The file must be an XHTML valid document.
*
* @type String
* @default "layout.xhtml"
*/
"guiMarkup": "layout.xhtml",
/**
* The interface style file.
*
* @type String
* @default "style.css"
*/
"guiStyle": "style.css",
/**
* The interface script file.
*
* @type String
* @default script.js
*/
"guiScript": "script.js",
/**
* The image viewport width. Make sure the value is a CSS length, like "50%",
* "450px" or "30em".
*
* <p>Note: the GUI implementation might ignore this value.
*
* @type String
* @default "100%"
*/
"viewportWidth": "100%",
/**
* The image viewport height. Make sure the value is a CSS length, like "50%",
* "450px" or "30em".
*
* <p>Note: the GUI implementation might ignore this value.
*
* @type String
* @default "400px"
* 672px
*/
"viewportHeight": "82%",
/**
* Image save quality for the JPEG format.
*
* @type Number
* @default 0.9
*/
"jpegSaveQuality": 0.9,
/**
* The default image width.
*
* @type Number
* @default 400
* 945
*/
"imageWidth": 945,
/**
* The default image width.
*
* @type Number
* @default 300
*/
"imageHeight": 800,
/**
* Image preload. The image you want to display when PaintWeb loads. This must
* be a reference to an HTML Image element.
*
* @type HTMLImageElement
* @default null
*/
"imagePreload": null,
/**
* Default background color.
*
* @type CSS3Color
* @default "#fff"
*/
"backgroundColor": "#fff",
/**
* Default fill style.
*
* @type CSS3Color-rgba functional notation
* @default "rgba(0,0,0,1)"
*/
"fillStyle": "rgba(0,0,255,1)",
/**
* Default stroke style.
*
* @type CSS3Color-rgba functional notation
* @default "rgba(0,0,255,1)"
*/
"strokeStyle": "rgba(0,0,0,1)",
/**
* Enable checkers background. This tells the user interface to render
* checkers in the image background. These are visible only when parts of
* the image being edited are transparent.
*
* <p>If the device you are running PaintWeb on has limited resources,
* disabling the checkers background should improve the drawing performance.
*
* @type Boolean
* @default true
*/
"checkersBackground": true,
/**
* GUI placeholder element. This element will hold all the PaintWeb interface
* elements.
*
* <p>For a successful initialization of PaintWeb, you must define this
* configuration value programatically from your scripts.
*
* @type Element
* @default null
*/
"guiPlaceholder": null,
/**
* Shape drawing "type": filled, only stroke, or both. Possible values:
* "filled", "stroke" or "both".
*
* @type String
* @default "both"
*/
"shapeType": "both",
/**
* Number of available history steps.
*
* @type Number
* @default 10
*/
"historyLimit": 10,
/**
* Zoom factor when the user increases/decreases the image zoom level.
*
* @type Number
* @default 0.05
*/
// 0.1 means 10% zoom. it's modified with zoom step in layout.xhtml.
"imageZoomStep": 0.01,
/**
* The maximum allowed image zoom level.
*
* @type Number
* @default 4
*/
// 4 means 400% zoom.
"imageZoomMax": 2,
/**
* The minimum allowed image zoom level.
*
* @type Number
* @default 0.2
*/
// 0.2 means 20% zoom.
"imageZoomMin": 0.2,
/**
* The image zoom control keys, for zoom in, zoom out and zoom reset.
* @type Object
*/
"imageZoomKeys": {
"in": "+",
"out": "-",
"reset": "*"
},
/**
* Holds the list of drawing tools you want to load.
* @type Array
*/
/*FIXME: */
/*"tools": ["bcurve", "cbucket", "cpicker", "ellipse", "eraser", "hand", "insertimg", "line", "pencil", "polygon", "rectangle", "selection", "text"],*/
"tools": ["rectangle", "selection"],
/**
* Tools folder.
* @type String
* @default "tools"
*/
"toolsFolder": "tools",
/**
* The default tool ID.
*
* @type String
* @default "line"
* @see this.tools The array holding the list of drawing tools you want
* loaded.
*/
"toolDefault": "line",
/**
* Tool drawing delay (milliseconds). Some tools delay their drawing
* operations for performance reasons.
*
* @type Number
* @default 80
*/
"toolDrawDelay": 80,
/**
* Holds the list of extensions you want to load.
* @type Array
*/
"extensions": ["colormixer", "mousekeys"],
/**
* Extensions folder.
*
* @type String
* @default "extensions"
*/
"extensionsFolder": "extensions",
/**
* @namespace Line tool options.
*/
"line": {
/**
* Line cap. Possible values: "butt", "round", "square".
*
* @type String
* @default "round"
*/
"lineCap": "round",
/**
* Line join. Possible values: "round", "bevel", "miter".
*
* @type String
* @default "round"
*/
"lineJoin": "round",
/**
* Line width.
*
* @type Number
* @default 1
*/
"lineWidth": 1,
/**
* Miter limit.
*
* @type Number
* @default 10
*/
"miterLimit": 10
},
/**
* @namespace Shadow options.
*/
"shadow": {
/**
* Tells if a shadow should render or not.
*
* @type Boolean
* @default false
*/
"enable": false,
/**
* Shadow color
*
* @type CSS3Color-rgba() functional notation
* @default "rgba(0,0,0,1)"
*/
"shadowColor": "rgba(0,0,0,1)",
/**
* Shadow blur.
*
* @type Number
* @default 5
*/
"shadowBlur": 5,
/**
* Shadow offset X.
*
* @type Number
* @default 5
*/
"shadowOffsetX": 5,
/**
* Shadow offset %.
*
* @type Number
* @default 5
*/
"shadowOffsetY": 5
},
/**
* @namespace Selection tool options.
*/
"selection": {
/**
* Selection transformation mode. This tells if any drag/resize would also
* affect the selected pixels or not.
*
* @type Boolean
* @default false
*/
"transform": false,
/**
* Transparent selection.
*
* @type Boolean
* @default true
*/
"transparent": true,
/**
* Selection marquee border width.
*
* @type Number
* @default 3
*/
"borderWidth": 3,
/**
* Keyboard shortcuts for several selection-related commands.
* @type Object
*/
"keys": {
"selectionCrop": "Control K",
"selectionDelete": "Delete",
"selectionDrop": "Escape",
"selectionFill": "Alt Backspace",
"transformToggle": "Enter"
}
},
/**
* Text tool options.
* @type Object
*/
"text": {
"bold": false,
"italic": false,
/**
* The default list of font families available in font family drop-down.
* @type Array
*/
"fontFamilies": ["sans-serif", "serif", "cursive", "fantasy", "monospace"],
/**
* The font family used for rendering the text.
* @type String
* @default "sans-serif"
*/
"fontFamily": "sans-serif",
"fontSize": 36,
/**
* Horizontal text alignment. Possible values: "left", "center", "right".
*
* <p>Note that the Canvas Text API also defines the "start" and "end"
* values, which are not "supported" by PaintWeb.
*
* @type String
* @default "left"
*/
"textAlign": "left",
/**
* Vertical text alignment. Possible values: "top", "hanging", "middle",
* "alphabetic", "ideographic", or "bottom".
*
* @type String
* @default "alphabetic"
*/
"textBaseline": "top"
},
/**
* @namespace Color Mixer extension configuration.
*/
"colormixer": {
/**
* Holds the minimum and maximum value for each color channel input field.
* The value incrementation step is also included - this is used the user
* presses the up/down arrow keys in the input of the color channel.
*
* @type Object
*/
"inputValues": {
// RGB
// order: minimum, maximum, step
"red": [0, 255, 1],
"green": [0, 255, 1],
"blue": [0, 255, 1],
// HSV
// Hue - degrees
"hue": [0, 360, 1],
"sat": [0, 255, 1],
"val": [0, 255, 1],
// CMYK - all are percentages
"cyan": [0, 100, 1],
"magenta": [0, 100, 1],
"yellow": [0, 100, 1],
"black": [0, 100, 1],
// CIE Lab
// cie_l = Lightness, it's a percentage value
// cie_a and cie_b are the color-opponent dimensions
"cie_l": [ 0, 100, 1],
"cie_a": [ -86, 98, 1],
"cie_b": [-107, 94, 1],
"alpha": [0, 100, 1]
},
/**
* CIE Lab configuration.
* @type Object
*/
"lab": {
// The RGB working space is sRGB which has the reference white point of
// D65.
// These are the chromaticity coordinates for the red, green and blue
// primaries.
"x_r": 0.64,
"y_r": 0.33,
"x_g": 0.3,
"y_g": 0.6,
"x_b": 0.13,
"y_b": 0.06,
// Standard observer: D65 (daylight), 2° (CIE 1931).
// Chromaticity coordinates.
"ref_x": 0.31271,
"ref_y": 0.32902,
// This is the calculated reference white point (xyY to XYZ) for D65, also
// known as the reference illuminant tristimulus.
// These values are updated based on chromaticity coordinates, during
// initialization.
"w_x": 0.95047,
"w_y": 1,
"w_z": 1.08883,
// The 3x3 matrix used for multiplying the RGB values when converting RGB
// to XYZ.
// These values are updated based on the chromaticity coordinates, during
// initialization.
"m": [ 0.412424, 0.212656, 0.0193324,
0.357579, 0.715158, 0.119193,
0.180464, 0.0721856, 0.950444],
// The same matrix, but inverted. This is used for the XYZ to RGB conversion.
"m_i": [ 3.24071, -0.969258, 0.0556352,
-1.53726, 1.87599, -0.203996,
-0.498571, 0.0415557, 1.05707]
},
/**
* Slider width. This value must be relative to the color space
* visualisation canvas element: 1 means full width, 0.5 means half width,
* etc.
*
* @type Number
* @default 0.10 (which is 10% of the canvas element width)
*/
"sliderWidth": 0.10,
/**
* Spacing between the slider and the color chart.
*
* @type Number
* @default 0.03 (which is 3% of the canvas element width)
*/
"sliderSpacing": 0.03,
/**
* Holds the list of color palettes.
* @type Object
*/
"colorPalettes": {
"_saved" : {
// Color values are: red, green, blue. All three channels have values
// ranging between 0 and 1.
"colors" : [[1,1,1], [1,1,0], [1,0,1], [0,1,1], [1,0,0], [0,1,0], [0,0,1], [0,0,0]]
},
"windows" : {
"file" : "colors/windows.json"
},
"macos" : {
"file" : "colors/macos.json"
},
"web" : {
"file" : "colors/web.json"
}
},
"paletteDefault": "windows"
},
/**
* @namespace Holds the MouseKeys extension options.
*/
"mousekeys": {
/**
* The mouse keys movement acceleration.
*
* @type Number
* @default 0.1
* @see PaintMouseKeys The MouseKeys extension.
*/
"accel": 0.1,
/**
* Holds the list of actions, associated to keyboard shortcuts.
*
* @type Object
*/
// We make sure the number keys on the NumPad also work when the Shift key
// is down.
"actions": {
"ButtonToggle": [0],
"SouthWest": [1],
"South": [2],
"SouthEast": [3],
"West": [4],
"ButtonClick": [5],
"East": [6],
"NorthWest": [7],
"North": [8],
"NorthEast": [9]
/*
You might want Shift+NumPad keys as well ...
Shift+Arrows breaks spatial navigation in Opera.
"ButtonToggle": [0, "Shift Insert"],
"SouthWest": [1, "Shift End"],
"South": [2, "Shift Down"],
"SouthEast": [3, "Shift PageDown"],
"West": [4, "Shift Left"],
"ButtonClick": [5, "Shift Clear"],
"East": [6, "Shift Right"],
"NorthWest": [7, "Shift Home"],
"North": [8, "Shift Up"],
"NorthEast": [9, "Shift PageUp"]
*/
}
},
/**
* Keyboard shortcuts associated to drawing tools and other actions.
*
* @type Object
* @see PaintTools The object holding all the drawing tools.
*/
"keys": {
// Use "command": "id" to execute some command.
"Control Z": { "command": "historyUndo" },
"Control Y": { "command": "historyRedo" },
"Control N": { "command": "imageClear" },
"Control S": { "command": "imageSave" },
"Control A": { "command": "selectAll" },
"Control X": { "command": "selectionCut" },
"Shift Delete": { "command": "selectionCut" },
"Control C": { "command": "selectionCopy" },
"Control V": { "command": "clipboardPaste" },
// Use "toolActivate": "id" to activate the tool with the given ID.
"C": { "toolActivate": "cpicker" },
"E": { "toolActivate": "ellipse" },
"F": { "toolActivate": "cbucket" },
"G": { "toolActivate": "polygon" },
"H": { "toolActivate": "hand" },
"I": { "toolActivate": "insertimg" },
"L": { "toolActivate": "line" },
"O": { "toolActivate": "eraser" },
"P": { "toolActivate": "pencil" },
"R": { "toolActivate": "rectangle" },
"S": { "toolActivate": "selection" },
"T": { "toolActivate": "text" },
"V": { "toolActivate": "bcurve" },
// Miscellaneous commands.
"X": { "command": "swapFillStroke" },
"F1": { "command": "about" }
}
// vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix ft=javascript:
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,295 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-08-24 13:18:05 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the Bézier curve tool implementation.
*/
/**
* @class The Bézier curve tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.bcurve = function (app) {
var _self = this,
clearInterval = app.win.clearInterval,
config = app.config,
context = app.buffer.context,
gui = app.gui,
image = app.image,
mouse = app.mouse,
setInterval = app.win.setInterval,
snapXY = app.toolSnapXY;
/**
* Holds the points in the Bézier curve being drawn.
*
* @private
* @type Array
*/
var points = [];
/**
* The interval ID used for invoking the drawing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Tells if the <kbd>Shift</kbd> key is down or not. This is used by the
* drawing function.
*
* @private
* @type Boolean
* @default false
*/
var shiftKey = false;
/**
* Tells if the drawing canvas needs to be updated or not.
*
* @private
* @type Boolean
* @default false
*/
var needsRedraw = false;
/**
* The tool deactivation method, used for clearing the buffer.
*/
this.deactivate = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
if (points.length > 0) {
context.clearRect(0, 0, image.width, image.height);
}
needsRedraw = false;
points = [];
return true;
};
/**
* The <code>mousedown</code> event handler.
*
* @param {Event} ev The DOM Event object.
*/
this.mousedown = function (ev) {
if (points.length === 0) {
gui.statusShow('bcurveSnapping');
points.push([mouse.x, mouse.y]);
}
if (!timer) {
timer = setInterval(_self.draw, config.toolDrawDelay);
}
shiftKey = ev.shiftKey;
needsRedraw = false;
return true;
};
/**
* Store the <kbd>Shift</kbd> key state which is used by the drawing function.
*
* @param {Event} ev The DOM Event object.
*/
this.mousemove = function (ev) {
shiftKey = ev.shiftKey;
needsRedraw = true;
};
/**
* Draw the Bézier curve, using the available points.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
if (!needsRedraw) {
return;
}
var n = points.length;
// Add the temporary point while the mouse button is down.
if (mouse.buttonDown) {
if (shiftKey && n === 1) {
snapXY(points[0][0], points[0][1]);
}
points.push([mouse.x, mouse.y]);
n++;
}
var p0 = points[0],
p1 = points[1],
p2 = points[2],
p3 = points[3] || points[2];
if (mouse.buttonDown) {
points.pop();
}
context.clearRect(0, 0, image.width, image.height);
if (!n) {
needsRedraw = false;
return;
}
// Draw the main line
if (n === 2) {
context.beginPath();
context.moveTo(p0[0], p0[1]+2);
context.lineTo(p1[0], p1[1]+2);
if (config.shapeType === 'fill') {
var lineWidth = context.lineWidth,
strokeStyle = context.strokeStyle;
context.lineWidth = 1;
context.strokeStyle = context.fillStyle;
}
context.stroke();
context.closePath();
if (config.shapeType === 'fill') {
context.lineWidth = lineWidth;
context.strokeStyle = strokeStyle;
}
needsRedraw = false;
return;
}
// Draw the Bézier curve
context.beginPath();
context.moveTo(p0[0], p0[1]);
context.bezierCurveTo(
p2[0], p2[1],
p3[0], p3[1],
p1[0], p1[1]);
if (config.shapeType !== 'stroke') {
context.fill();
}
if (config.shapeType !== 'fill') {
context.stroke();
}
context.closePath();
needsRedraw = false;
};
/**
* The <code>mouseup</code> event handler. This method stores the current
* mouse coordinates as a point to be used for drawing the Bézier curve.
*
* @param {Event} ev The DOM Event object.
*/
this.mouseup = function (ev) {
var n = points.length;
// Allow click+mousemove+click, not only mousedown+mousemove+mouseup.
// Do this only for the start point.
if (n === 1 && mouse.x === points[0][0] && mouse.y === points[0][1]) {
mouse.buttonDown = true;
return true;
}
if (timer) {
clearInterval(timer);
timer = null;
}
if (n === 1 && ev.shiftKey) {
snapXY(points[0][0], points[0][1]);
}
// We need 4 points to draw the Bézier curve: start, end, and two control
// points.
if (n < 4) {
points.push([mouse.x, mouse.y]);
needsRedraw = true;
n++;
}
// Make sure the canvas is up-to-date.
shiftKey = ev.shiftKey;
_self.draw();
if (n === 2 || n === 3) {
gui.statusShow('bcurveControlPoint' + (n-1));
} else if (n === 4) {
gui.statusShow('bcurveActive');
app.layerUpdate();
points = [];
}
return true;
};
/**
* The <code>keydown</code> event handler. This method allows the user to
* press the <kbd>Escape</kbd> key to cancel the current drawing operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the keyboard shortcut was recognized, or false
* if not.
*/
this.keydown = function (ev) {
if (!points.length || ev.kid_ !== 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
context.clearRect(0, 0, image.width, image.height);
points = [];
needsRedraw = false;
mouse.buttonDown = false;
gui.statusShow('bcurveActive');
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,225 @@
/*
* Copyright (C) 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-11-10 20:12:34 +0200 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the color bucket tool implementation, also known as the
* flood fill tool.
*/
/**
* @class The color bucket tool.
*
* The implementation here is based on the seed fill algorithm of Paul S.
* Heckbert (1990).
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.cbucket = function (app) {
var _self = this,
config = app.config,
layer = app.layer.context,
buffer = app.buffer.context,
iwidth = app.image.width,
iheight = app.image.height,
mouse = app.mouse;
var stackMax = 10000; // maximum depth of stack
var lines = []; // stack of lines
var pixelNew, layerpix;
/**
* The <code>preActivate</code> event handler. This method checks if the
* browser implements the <code>getImageData()</code> and
* <code>putImageData()</code> context methods. If not, the color bucket tool
* cannot be used.
*
* @returns {Boolean} True if the drawing tool can be activated, or false
* otherwise.
*/
this.preActivate = function () {
// The latest versions of all browsers which implement Canvas, also
// implement the getImageData() method. This was only a problem with some
// old versions (eg. Opera 9.2).
if (!layer.getImageData || !layer.putImageData) {
alert(app.lang.errorCbucketUnsupported);
return false;
} else {
return true;
}
};
/**
* The <code>activate</code> event handler. Canvas shadow rendering is
* disabled.
*/
this.activate = function () {
app.shadowDisallow();
};
/**
* The <code>deactivate</code> event handler. Canvas shadow rendering is
* allowed once again.
*/
this.deactivate = function () {
app.shadowAllow();
};
/**
* The <code>click</code> and <code>contextmenu</code> event handler. This
* method performs the flood fill operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the image was modified, or false otherwise.
*/
this.click = function (ev) {
// Allow the user to right-click or hold down the Shift key to use the
// border color for filling the image.
if (ev.type === 'contextmenu' || ev.button === 2 || ev.shiftKey) {
var fillStyle = buffer.fillStyle;
buffer.fillStyle = buffer.strokeStyle;
buffer.fillRect(0, 0, 1, 1);
buffer.fillStyle = fillStyle;
} else {
buffer.fillRect(0, 0, 1, 1);
}
// Instead of parsing the fillStyle ...
pixelNew = buffer.getImageData(0, 0, 1, 1);
pixelNew = [pixelNew.data[0], pixelNew.data[1], pixelNew.data[2],
pixelNew.data[3]];
buffer.clearRect(0, 0, 1, 1);
var pixelOld = layer.getImageData(mouse.x, mouse.y, 1, 1).data;
pixelOld = pixelOld[0] + ';' + pixelOld[1] + ';' + pixelOld[2] + ';'
+ pixelOld[3];
if (pixelOld === pixelNew.join(';')) {
return false;
}
fill(mouse.x, mouse.y, pixelOld);
app.historyAdd();
return true;
};
this.contextmenu = this.click;
/**
* Fill the image with the current fill color, starting from the <var>x</var>
* and <var>y</var> coordinates.
*
* @private
*
* @param {Number} x The x coordinate for the starting point.
* @param {Number} y The y coordinate for the starting point.
* @param {String} pixelOld The old pixel value.
*/
var fill = function (x, y, pixelOld) {
var start, x1, x2, dy, tmp, idata;
pushLine(y, x, x, 1); // needed in some cases
pushLine(y + 1, x, x, -1); // seed segment (popped 1st)
while (lines.length > 0) {
// pop segment off stack and fill a neighboring scan line
tmp = lines.pop();
dy = tmp[3];
y = tmp[0] + dy;
x1 = tmp[1];
x2 = tmp[2];
layerpix = null;
idata = layer.getImageData(0, y, iwidth, 1);
layerpix = idata.data;
// segment of scan line y-dy for x1 <= x <= x2 was previously filled, now
// explore adjacent pixels in scan line y
for (x = x1; x >= 0 && pixelRead(x) === pixelOld; x--) {
pixelWrite(x);
}
if (x >= x1) {
for (x++; x <= x2 && pixelRead(x) !== pixelOld; x++);
start = x;
if (x > x2) {
layer.putImageData(idata, 0, y);
continue;
}
} else {
start = x + 1;
if (start < x1) {
pushLine(y, start, x1 - 1, -dy); // leak on left?
}
x = x1 + 1;
}
do {
for (; x < iwidth && pixelRead(x) === pixelOld; x++) {
pixelWrite(x);
}
pushLine(y, start, x - 1, dy);
if (x > (x2 + 1)) {
pushLine(y, x2 + 1, x - 1, -dy); // leak on right?
}
for (x++; x <= x2 && pixelRead(x) !== pixelOld; x++);
start = x;
} while (x <= x2);
layer.putImageData(idata, 0, y);
}
layerpix = null;
idata = null;
};
var pushLine = function (y, xl, xr, dy) {
if (lines.length < stackMax && (y+dy) >= 0 && (y+dy) < iheight) {
lines.push([y, xl, xr, dy]);
}
};
var pixelRead = function (x) {
var r = 4 * x;
return layerpix[r] + ';' + layerpix[r+1] + ';' + layerpix[r+2] + ';'
+ layerpix[r+3];
};
var pixelWrite = function (x) {
var r = 4 * x;
layerpix[r] = pixelNew[0];
layerpix[r+1] = pixelNew[1];
layerpix[r+2] = pixelNew[2];
layerpix[r+3] = pixelNew[3];
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,314 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-07-02 15:37:38 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the color picker implementation.
*/
/**
* @class The color picker tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.cpicker = function (app) {
var _self = this,
colormixer = app.extensions.colormixer,
context = app.layer.context,
gui = app.gui,
lang = app.lang,
MathRound = Math.round,
mouse = app.mouse;
/**
* Holds the ID of the previously active tool. Once the user completes the
* color picking operation, the previous tool is activated.
*
* @private
* @type String
*/
var prevTool = null;
/**
* Holds a reference to the target color input. This is a GUI color input
* component.
*
* @private
* @type pwlib.guiColorInput
*/
var targetInput = null;
/**
* Holds the previous color values - before the user started picking
* a different color.
*
* @private
* @type Object
*/
var prevColor = null;
/**
* Tells if the color mixer is active for the current target input.
*
* @private
* @type Boolean
*/
var colormixerActive = false;
/**
* Tells if the current color values are accepted by the user. This value is
* used by the tool deactivation code.
*
* @private
* @type Boolean
*/
var colorAccepted = false;
/**
* The <code>preActivate</code> event handler. This method checks if the
* browser implements the <code>getImageData()</code> context method. If not,
* the color picker tool cannot be used.
*/
this.preActivate = function () {
// The latest versions of all browsers which implement Canvas, also
// implement the getImageData() method. This was only a problem with some
// old versions (eg. Opera 9.2).
if (!context.getImageData) {
alert(lang.errorCpickerUnsupported);
return false;
}
if (app.tool && app.tool._id) {
prevTool = app.tool._id;
}
return true;
};
/**
* The <code>activate</code> event handler. This method determines the current
* target input in the Color Mixer, if any. Canvas shadow rendering is
* disallowed.
*/
this.activate = function () {
// When the color mixer panel is active, the color picker uses the same
// target input.
if (colormixer && colormixer.targetInput) {
targetInput = gui.colorInputs[colormixer.targetInput.id];
}
if (targetInput) {
gui.statusShow('cpicker_' + targetInput.id);
} else {
gui.statusShow('cpickerNormal');
}
app.shadowDisallow();
};
/**
* The <code>deactivate</code> event handler. This method allows shadow
* rendering again, and resets the color input values if the user did not
* accept the new color.
*/
this.deactivate = function () {
if (!colorAccepted && targetInput && prevColor) {
updateColor(null, true);
}
app.shadowAllow();
};
/**
* The <code>mousedown</code> event handler. This method starts the color
* picking operation.
*
* @param {Event} ev The DOM Event object.
*/
this.mousedown = function (ev) {
// We check again, because the user might have opened/closed the color
// mixer.
if (colormixer && colormixer.targetInput) {
targetInput = gui.colorInputs[colormixer.targetInput.id];
}
if (targetInput) {
colormixerActive = true;
gui.statusShow('cpicker_' + targetInput.id);
} else {
colormixerActive = false;
gui.statusShow('cpickerNormal');
// The context menu (right-click). This is unsupported by Opera.
// Also allow Shift+Click for changing the stroke color (making it easier for Opera users).
if (ev.button === 2 || ev.shiftKey) {
targetInput = gui.colorInputs.strokeStyle;
} else {
targetInput = gui.colorInputs.fillStyle;
}
}
updatePrevColor();
_self.mousemove = updateColor;
updateColor(ev);
return true;
};
/**
* Perform color update. This function updates the target input or the Color
* Mixer to hold the color value under the mouse - it actually performs the
* color picking operation.
*
* <p>This function is also the <code>mousemove</code> event handler for this
* tool.
*
* @param {Event} ev The DOM Event object.
* @param {Boolean} [usePrevColor=false] Tells the function to use the
* previous color values we have stored. This is used when the user cancels
* the color picking operation.
*/
function updateColor (ev, usePrevColor) {
if (!targetInput) {
return;
}
var p = usePrevColor ? prevColor :
context.getImageData(mouse.x, mouse.y, 1, 1),
color = {
red: p.data[0] / 255,
green: p.data[1] / 255,
blue: p.data[2] / 255,
alpha: (p.data[3] / 255).toFixed(3)
};
if (colormixerActive) {
colormixer.color.red = color.red;
colormixer.color.green = color.green;
colormixer.color.blue = color.blue;
colormixer.color.alpha = color.alpha;
colormixer.update_color('rgb');
} else {
targetInput.updateColor(color);
}
};
/**
* The <code>mouseup</code> event handler. This method completes the color
* picking operation, and activates the previous tool.
*
* <p>The {@link pwlib.appEvent.configChange} application event is also
* dispatched for the configuration property associated to the target input.
*
* @param {Event} ev The DOM Event object.
*/
this.mouseup = function (ev) {
if (!targetInput) {
return false;
}
delete _self.mousemove;
updateColor(ev);
colorAccepted = true;
if (!colormixerActive) {
var color = targetInput.color,
configProperty = targetInput.configProperty,
configGroup = targetInput.configGroup,
configGroupRef = targetInput.configGroupRef,
prevVal = configGroupRef[configProperty],
newVal = 'rgba(' + MathRound(color.red * 255) + ',' +
MathRound(color.green * 255) + ',' +
MathRound(color.blue * 255) + ',' +
color.alpha + ')';
if (prevVal !== newVal) {
configGroupRef[configProperty] = newVal;
app.events.dispatch(new pwlib.appEvent.configChange(newVal, prevVal,
configProperty, configGroup, configGroupRef));
}
}
if (prevTool) {
app.toolActivate(prevTool, ev);
}
return true;
};
/**
* The <code>keydown</code> event handler. This method allows the user to
* press the <kbd>Escape</kbd> key to cancel the color picking operation. By
* doing so, the original color values are restored.
*
* @param {Event} ev The DOM Event object.
* @returns {Boolean} True if the keyboard shortcut was recognized, or false
* if not.
*/
this.keydown = function (ev) {
if (!prevTool || ev.kid_ !== 'Escape') {
return false;
}
mouse.buttonDown = false;
app.toolActivate(prevTool, ev);
return true;
};
/**
* The <code>contextmenu</code> event handler. This method only cancels the
* context menu.
*/
// Unfortunately, the contextmenu event is unsupported by Opera.
this.contextmenu = function () {
return true;
};
/**
* Store the color values from the target color input, before this tool
* changes the colors. The previous color values are used when the user
* decides to cancel the color picking operation.
* @private
*/
function updatePrevColor () {
// If the color mixer panel is visible, then we store the color values from
// the color mixer, instead of those from the color input object.
var color = colormixerActive ? colormixer.color : targetInput.color;
prevColor = {
width: 1,
height: 1,
data: [
MathRound(color.red * 255),
MathRound(color.green * 255),
MathRound(color.blue * 255),
color.alpha * 255
]
};
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,285 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-07-01 18:44:56 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the ellipse tool implementation.
*/
/**
* @class The ellipse tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.ellipse = function (app) {
var _self = this,
clearInterval = app.win.clearInterval,
config = app.config,
context = app.buffer.context,
gui = app.gui,
image = app.image,
MathMax = Math.max,
MathMin = Math.min,
mouse = app.mouse,
setInterval = app.win.setInterval;
/**
* The interval ID used for invoking the drawing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Tells if the <kbd>Shift</kbd> key is down or not. This is used by the
* drawing function.
*
* @private
* @type Boolean
* @default false
*/
var shiftKey = false;
/**
* Tells if the drawing canvas needs to be updated or not.
*
* @private
* @type Boolean
* @default false
*/
var needsRedraw = false;
var K = 4*((Math.SQRT2-1)/3);
/**
* Holds the starting point on the <var>x</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var x0 = 0;
/**
* Holds the starting point on the <var>y</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var y0 = 0;
/**
* Tool deactivation event handler.
*/
this.deactivate = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
if (mouse.buttonDown) {
context.clearRect(0, 0, image.width, image.height);
}
needsRedraw = false;
return true;
};
/**
* Initialize the drawing operation.
*
* @param {Event} ev The DOM Event object.
*/
this.mousedown = function (ev) {
// The mouse start position
x0 = mouse.x;
y0 = mouse.y;
if (!timer) {
timer = setInterval(_self.draw, config.toolDrawDelay);
}
shiftKey = ev.shiftKey;
needsRedraw = false;
gui.statusShow('ellipseMousedown');
return true;
};
/**
* Store the <kbd>Shift</kbd> key state which is used by the drawing function.
*
* @param {Event} ev The DOM Event object.
*/
this.mousemove = function (ev) {
shiftKey = ev.shiftKey;
needsRedraw = true;
};
/**
* Perform the drawing operation. This function is called every few
* milliseconds.
*
* <p>Hold down the <kbd>Shift</kbd> key to draw a circle.
* <p>Press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
if (!needsRedraw) {
return;
}
context.clearRect(0, 0, image.width, image.height);
var rectx0 = MathMin(mouse.x, x0),
rectx1 = MathMax(mouse.x, x0),
recty0 = MathMin(mouse.y, y0),
recty1 = MathMax(mouse.y, y0);
/*
ABCD - rectangle
A(rectx0, recty0), B(rectx1, recty0), C(rectx1, recty1), D(rectx0, recty1)
*/
var w = rectx1-rectx0,
h = recty1-recty0;
if (!w || !h) {
needsRedraw = false;
return;
}
// Constrain the ellipse to be a circle
if (shiftKey) {
if (w > h) {
recty1 = recty0+w;
if (recty0 == mouse.y) {
recty0 -= w-h;
recty1 -= w-h;
}
h = w;
} else {
rectx1 = rectx0+h;
if (rectx0 == mouse.x) {
rectx0 -= h-w;
rectx1 -= h-w;
}
w = h;
}
}
// Ellipse radius
var rx = w/2,
ry = h/2;
// Ellipse center
var cx = rectx0+rx,
cy = recty0+ry;
// Ellipse radius*Kappa, for the Bézier curve control points
rx *= K;
ry *= K;
context.beginPath();
// startX, startY
context.moveTo(cx, recty0);
// Control points: cp1x, cp1y, cp2x, cp2y, destx, desty
// go clockwise: top-middle, right-middle, bottom-middle, then left-middle
context.bezierCurveTo(cx + rx, recty0, rectx1, cy - ry, rectx1, cy);
context.bezierCurveTo(rectx1, cy + ry, cx + rx, recty1, cx, recty1);
context.bezierCurveTo(cx - rx, recty1, rectx0, cy + ry, rectx0, cy);
context.bezierCurveTo(rectx0, cy - ry, cx - rx, recty0, cx, recty0);
if (config.shapeType != 'stroke') {
context.fill();
}
if (config.shapeType != 'fill') {
context.stroke();
}
context.closePath();
needsRedraw = false;
};
/**
* End the drawing operation, once the user releases the mouse button.
*
* @param {Event} ev The DOM Event object.
*/
this.mouseup = function (ev) {
// Allow click+mousemove, not only mousedown+move+up
if (mouse.x == x0 && mouse.y == y0) {
mouse.buttonDown = true;
return true;
}
if (timer) {
clearInterval(timer);
timer = null;
}
shiftKey = ev.shiftKey;
_self.draw();
app.layerUpdate();
gui.statusShow('ellipseActive');
return true;
};
/**
* Allows the user to press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the drawing operation was cancelled, or false if
* not.
*/
this.keydown = function (ev) {
if (!mouse.buttonDown || ev.kid_ != 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
context.clearRect(0, 0, image.width, image.height);
mouse.buttonDown = false;
needsRedraw = false;
gui.statusShow('ellipseActive');
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,231 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-07-29 20:34:06 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the eraser tool implementation.
*/
/**
* @class The eraser tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.eraser = function (app) {
var _self = this,
bufferContext = app.buffer.context,
clearInterval = app.win.clearInterval,
config = app.config,
history = app.history.pos,
image = app.image,
layerContext = app.layer.context,
mouse = app.mouse,
setInterval = app.win.setInterval;
/**
* The interval ID used for running the erasing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Holds the points needed to be drawn. Each point is added by the
* <code>mousemove</code> event handler.
*
* @private
* @type Array
*/
var points = [];
/**
* Holds the starting point on the <var>x</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var x0 = 0;
/**
* Holds the starting point on the <var>y</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var y0 = 0;
var globalOp_ = null,
lineWidth_ = null;
/**
* The tool deactivation event handler. This function clears timers, clears
* the canvas and allows shadows to be rendered again.
*/
this.deactivate = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
if (mouse.buttonDown) {
if (globalOp_) {
layerContext.globalCompositeOperation = globalOp_;
}
if (lineWidth_) {
layerContext.lineWidth = lineWidth_;
}
app.historyGoto(history.pos);
}
points = [];
// Allow Canvas shadows.
app.shadowAllow();
};
/**
* The tool activation event handler. This is run after the tool construction
* and after the deactivation of the previous tool. This function simply
* disallows the rendering of shadows.
*/
this.activate = function () {
// Do not allow Canvas shadows.
app.shadowDisallow();
};
/**
* Initialize the drawing operation.
*/
this.mousedown = function () {
globalOp_ = layerContext.globalCompositeOperation;
lineWidth_ = layerContext.lineWidth;
layerContext.globalCompositeOperation = 'destination-out';
layerContext.lineWidth = bufferContext.lineWidth;
x0 = mouse.x;
y0 = mouse.y;
points = [];
if (!timer) {
timer = setInterval(_self.draw, config.toolDrawDelay);
}
return true;
};
/**
* Save the mouse coordinates in the array.
*/
this.mousemove = function () {
if (mouse.buttonDown) {
points.push(mouse.x, mouse.y);
}
};
/**
* Draw the points in the stack. This function is called every few
* milliseconds.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
var i = 0, n = points.length;
if (!n) {
return;
}
layerContext.beginPath();
layerContext.moveTo(x0, y0);
while (i < n) {
x0 = points[i++];
y0 = points[i++];
layerContext.lineTo(x0, y0);
}
layerContext.stroke();
layerContext.closePath();
points = [];
};
/**
* End the drawing operation, once the user releases the mouse button.
*/
this.mouseup = function () {
if (mouse.x == x0 && mouse.y == y0) {
points.push(x0+1, y0+1);
}
if (timer) {
clearInterval(timer);
timer = null;
}
_self.draw();
layerContext.globalCompositeOperation = globalOp_;
layerContext.lineWidth = lineWidth_;
app.historyAdd();
return true;
};
/**
* Allows the user to press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the drawing operation was cancelled, or false if
* not.
*/
this.keydown = function (ev) {
if (!mouse.buttonDown || ev.kid_ != 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
layerContext.globalCompositeOperation = globalOp_;
layerContext.lineWidth = lineWidth_;
mouse.buttonDown = false;
points = [];
app.historyGoto(history.pos);
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,216 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-06-15 20:27:08 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the hand tool implementation.
*/
/**
* @class The hand tool. This tool allows the user to drag the image canvas
* inside the viewport.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.hand = function (app) {
var _self = this,
bufferCanvas = app.buffer.canvas,
bufferStyle = bufferCanvas.style,
config = app.config;
clearInterval = app.win.clearInterval,
image = app.image,
MathRound = Math.round,
mouse = app.mouse,
viewport = app.gui.elems.viewport,
vheight = 0,
vwidth = 0,
setInterval = app.win.setInterval;
/**
* The interval ID used for invoking the viewport drag operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Tells if the viewport needs to be scrolled.
*
* @private
* @type Boolean
* @default false
*/
var needsScroll = false;
/**
* Holds the previous tool ID.
*
* @private
* @type String
*/
this.prevTool = null;
var x0 = 0, y0 = 0,
x1 = 0, y1 = 0,
l0 = 0, t0 = 0;
/**
* Tool preactivation event handler.
*
* @returns {Boolean} True if the tool can become active, or false if not.
*/
this.preActivate = function () {
if (!viewport) {
return false;
}
_self.prevTool = app.tool._id;
// Check if the image canvas can be scrolled within the viewport.
var cs = app.win.getComputedStyle(viewport, null),
bwidth = parseInt(bufferStyle.width),
bheight = parseInt(bufferStyle.height);
vwidth = parseInt(cs.width),
vheight = parseInt(cs.height);
if (vheight < bheight || vwidth < bwidth) {
return true;
} else {
return false;
}
};
/**
* Tool activation event handler.
*/
this.activate = function () {
bufferStyle.cursor = 'move';
app.shadowDisallow();
};
/**
* Tool deactivation event handler.
*/
this.deactivate = function (ev) {
if (timer) {
clearInterval(timer);
timer = null;
app.doc.removeEventListener('mousemove', ev_mousemove, false);
app.doc.removeEventListener('mouseup', ev_mouseup, false);
}
bufferStyle.cursor = '';
app.shadowAllow();
};
/**
* Initialize the canvas drag.
*
* @param {Event} ev The DOM event object.
*/
this.mousedown = function (ev) {
x0 = ev.clientX;
y0 = ev.clientY;
l0 = viewport.scrollLeft;
t0 = viewport.scrollTop;
needsScroll = false;
app.doc.addEventListener('mousemove', ev_mousemove, false);
app.doc.addEventListener('mouseup', ev_mouseup, false);
if (!timer) {
timer = setInterval(viewportScroll, config.toolDrawDelay);
}
return true;
};
/**
* The <code>mousemove</code> event handler. This simply stores the current
* mouse location.
*
* @param {Event} ev The DOM Event object.
*/
function ev_mousemove (ev) {
x1 = ev.clientX;
y1 = ev.clientY;
needsScroll = true;
};
/**
* Perform the canvas drag operation. This function is called every few
* milliseconds.
*
* <p>Press <kbd>Escape</kbd> to stop dragging and to get back to the previous
* tool.
*/
function viewportScroll () {
if (needsScroll) {
viewport.scrollTop = t0 - y1 + y0;
viewport.scrollLeft = l0 - x1 + x0;
needsScroll = false;
}
};
/**
* The <code>mouseup</code> event handler.
*/
function ev_mouseup (ev) {
if (timer) {
clearInterval(timer);
timer = null;
}
ev_mousemove(ev);
viewportScroll();
app.doc.removeEventListener('mousemove', ev_mousemove, false);
app.doc.removeEventListener('mouseup', ev_mouseup, false);
mouse.buttonDown = false;
};
/**
* Allows the user to press <kbd>Escape</kbd> to stop dragging the canvas, and
* to return to the previous tool.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the key was recognized, or false if not.
*/
this.keydown = function (ev) {
if (!_self.prevTool || ev.kid_ != 'Escape') {
return false;
}
app.toolActivate(_self.prevTool, ev);
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,378 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-07-06 16:20:38 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the "Insert image" tool implementation.
*/
// TODO: allow inserting images from a different host, using server-side magic.
/**
* @class The "Insert image" tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.insertimg = function (app) {
var _self = this,
canvasImage = app.image,
clearInterval = app.win.clearInterval,
config = app.config,
context = app.buffer.context,
gui = app.gui,
lang = app.lang,
MathAbs = Math.abs,
MathMin = Math.min,
MathRound = Math.round,
mouse = app.mouse,
setInterval = app.win.setInterval;
/**
* Holds the previous tool ID.
*
* @private
* @type String
*/
var prevTool = app.tool ? app.tool._id : null;
/**
* The interval ID used for invoking the drawing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Tells if the <kbd>Shift</kbd> key is down or not. This is used by the
* drawing function.
*
* @private
* @type Boolean
* @default false
*/
var shiftKey = false;
/**
* Tells if the drawing canvas needs to be updated or not.
*
* @private
* @type Boolean
* @default false
*/
var needsRedraw = false;
/**
* Holds the starting point on the <var>x</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var x0 = 0;
/**
* Holds the starting point on the <var>y</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var y0 = 0;
/**
* Tells if the image element loaded or not.
*
* @private
* @type Boolean
*/
var imageLoaded = false;
/**
* Holds the image aspect ratio, used by the resize method.
*
* @private
* @type Number
*/
var imageRatio = 1;
/**
* Holds the DOM image element.
*
* @private
* @type Element
*/
var imageElement = null;
/**
* Holds the image address.
* @type String
*/
if (!this.url) {
this.url = 'http://';
}
/**
* The tool preactivation code. This function asks the user to provide an URL
* to the image which is desired to be inserted into the canvas.
*
* @returns {Boolean} True if the URL provided is correct. False is returned
* if the URL is not provided or if it's incorrect. When false is returned the
* tool activation is cancelled.
*/
this.preActivate = function () {
if (!gui.elems.viewport) {
return false;
}
_self.url = prompt(lang.promptInsertimg, _self.url);
if (!_self.url || _self.url.toLowerCase() === 'http://') {
return false;
}
// Remember the URL.
pwlib.extend(true, _self.constructor.prototype, {url: _self.url});
if (!pwlib.isSameHost(_self.url, app.win.location.host)) {
alert(lang.errorInsertimgHost);
return false;
}
return true;
};
/**
* The tool activation event handler. This function is called once the
* previous tool has been deactivated.
*/
this.activate = function () {
imageElement = new Image();
imageElement.addEventListener('load', ev_imageLoaded, false);
imageElement.src = _self.url;
return true;
};
/**
* The tool deactivation event handler.
*/
this.deactivate = function () {
if (imageElement) {
imageElement = null;
}
if (timer) {
clearInterval(timer);
timer = null;
}
needsRedraw = false;
context.clearRect(0, 0, canvasImage.width, canvasImage.height);
return true;
};
/**
* The <code>load</code> event handler for the image element. This method
* makes sure the image dimensions are synchronized with the zoom level, and
* draws the image on the canvas.
*
* @private
*/
function ev_imageLoaded () {
// Did the image already load?
if (imageLoaded) {
return;
}
// The default position for the inserted image is the top left corner of the visible area, taking into consideration the zoom level.
var x = MathRound(gui.elems.viewport.scrollLeft / canvasImage.canvasScale),
y = MathRound(gui.elems.viewport.scrollTop / canvasImage.canvasScale);
context.clearRect(0, 0, canvasImage.width, canvasImage.height);
try {
context.drawImage(imageElement, x, y);
} catch (err) {
alert(lang.errorInsertimg);
return;
}
imageLoaded = true;
needsRedraw = false;
if (!timer) {
timer = setInterval(_self.draw, config.toolDrawDelay);
}
gui.statusShow('insertimgLoaded');
};
/**
* The <code>mousedown</code> event handler. This method stores the current
* mouse location and the image aspect ratio for later reuse by the
* <code>draw()</code> method.
*
* @param {Event} ev The DOM Event object.
*/
this.mousedown = function (ev) {
if (!imageLoaded) {
alert(lang.errorInsertimgNotLoaded);
return false;
}
x0 = mouse.x;
y0 = mouse.y;
// The image aspect ratio - used by the draw() method when the user holds
// the Shift key down.
imageRatio = imageElement.width / imageElement.height;
shiftKey = ev.shiftKey;
gui.statusShow('insertimgResize');
if (ev.stopPropagation) {
ev.stopPropagation();
}
};
/**
* The <code>mousemove</code> event handler.
*
* @param {Event} ev The DOM Event object.
*/
this.mousemove = function (ev) {
shiftKey = ev.shiftKey;
needsRedraw = true;
};
/**
* Perform the drawing operation. When the mouse button is not down, the user
* is allowed to pick where he/she wants to insert the image element, inside
* the canvas. Once the <code>mousedown</code> event is fired, this method
* allows the user to resize the image inside the canvas.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
if (!imageLoaded || !needsRedraw) {
return;
}
context.clearRect(0, 0, canvasImage.width, canvasImage.height);
// If the user is holding down the mouse button, then allow him/her to
// resize the image.
if (mouse.buttonDown) {
var w = MathAbs(mouse.x - x0),
h = MathAbs(mouse.y - y0),
x = MathMin(mouse.x, x0),
y = MathMin(mouse.y, y0);
if (!w || !h) {
needsRedraw = false;
return;
}
// If the Shift key is down, constrain the image to have the same aspect
// ratio as the original image element.
if (shiftKey) {
if (w > h) {
if (y == mouse.y) {
y -= w-h;
}
h = MathRound(w/imageRatio);
} else {
if (x == mouse.x) {
x -= h-w;
}
w = MathRound(h*imageRatio);
}
}
context.drawImage(imageElement, x, y, w, h);
} else {
// If the mouse button is not down, simply allow the user to pick where
// he/she wants to insert the image element.
context.drawImage(imageElement, mouse.x, mouse.y);
}
needsRedraw = false;
};
/**
* The <code>mouseup</code> event handler. This method completes the drawing
* operation by inserting the image in the layer canvas, and by activating the
* previous tool.
*
* @param {Event} ev The DOM Event object.
*/
this.mouseup = function (ev) {
if (!imageLoaded) {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
app.layerUpdate();
if (prevTool) {
app.toolActivate(prevTool, ev);
}
if (ev.stopPropagation) {
ev.stopPropagation();
}
};
/**
* The <code>keydown</code> event handler allows users to press the
* <kbd>Escape</kbd> key to cancel the drawing operation and return to the
* previous tool.
*
* @param {Event} ev The DOM Event object.
* @returns {Boolean} True if the key was recognized, or false if not.
*/
this.keydown = function (ev) {
if (!prevTool || ev.kid_ != 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
mouse.buttonDown = false;
app.toolActivate(prevTool, ev);
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,224 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-06-11 20:23:04 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the line tool implementation.
*/
/**
* @class The line tool.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.line = function (app) {
var _self = this,
clearInterval = app.win.clearInterval,
config = app.config,
context = app.buffer.context,
gui = app.gui,
image = app.image,
mouse = app.mouse,
setInterval = app.win.setInterval,
snapXY = app.toolSnapXY;
/**
* The interval ID used for invoking the drawing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Tells if the <kbd>Shift</kbd> key is down or not. This is used by the
* drawing function.
*
* @private
* @type Boolean
* @default false
*/
var shiftKey = false;
/**
* Tells if the drawing canvas needs to be updated or not.
*
* @private
* @type Boolean
* @default false
*/
var needsRedraw = false;
/**
* Holds the starting point on the <var>x</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var x0 = 0;
/**
* Holds the starting point on the <var>y</var> axis of the image, for the
* current drawing operation.
*
* @private
* @type Number
*/
var y0 = 0;
/**
* Tool deactivation event handler.
*/
this.deactivate = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
if (mouse.buttonDown) {
context.clearRect(0, 0, image.width, image.height);
}
needsRedraw = false;
return true;
};
/**
* Initialize the drawing operation, by storing the location of the pointer,
* the start position.
*
* @param {Event} ev The DOM Event object.
*/
this.mousedown = function (ev) {
x0 = mouse.x;
y0 = mouse.y;
if (!timer) {
timer = setInterval(_self.draw, config.toolDrawDelay);
}
shiftKey = ev.shiftKey;
needsRedraw = false;
gui.statusShow('lineMousedown');
return true;
};
/**
* Store the <kbd>Shift</kbd> key state which is used by the drawing function.
*
* @param {Event} ev The DOM Event object.
*/
this.mousemove = function (ev) {
shiftKey = ev.shiftKey;
needsRedraw = true;
};
/**
* Perform the drawing operation. This function is called every few
* milliseconds.
*
* <p>Hold down the <kbd>Shift</kbd> key to draw a straight
* horizontal/vertical line.
* <p>Press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
if (!needsRedraw) {
return;
}
context.clearRect(0, 0, image.width, image.height);
// Snapping on the X/Y axis.
if (shiftKey) {
snapXY(x0, y0);
}
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(mouse.x, mouse.y);
context.stroke();
context.closePath();
needsRedraw = false;
};
/**
* End the drawing operation, once the user releases the mouse button.
*
* @param {Event} ev The DOM Event object.
*/
this.mouseup = function (ev) {
// Allow users to click then drag, not only mousedown+drag+mouseup.
if (mouse.x == x0 && mouse.y == y0) {
mouse.buttonDown = true;
return true;
}
if (timer) {
clearInterval(timer);
timer = null;
}
shiftKey = ev.shiftKey;
_self.draw();
gui.statusShow('lineActive');
app.layerUpdate();
return true;
};
/**
* Allows the user to press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the drawing operation was cancelled, or false if
* not.
*/
this.keydown = function (ev) {
if (!mouse.buttonDown || ev.kid_ != 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
context.clearRect(0, 0, image.width, image.height);
mouse.buttonDown = false;
needsRedraw = false;
gui.statusShow('lineActive');
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

@ -0,0 +1,190 @@
/*
* Copyright (C) 2008, 2009 Mihai Şucan
*
* This file is part of PaintWeb.
*
* PaintWeb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PaintWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PaintWeb. If not, see <http://www.gnu.org/licenses/>.
*
* $URL: http://code.google.com/p/paintweb $
* $Date: 2009-06-15 15:25:29 +0300 $
*/
/**
* @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
* @fileOverview Holds the pencil tool implementation.
*/
/**
* @class The drawing pencil.
*
* @param {PaintWeb} app Reference to the main paint application object.
*/
pwlib.tools.pencil = function (app) {
var _self = this,
clearInterval = app.win.clearInterval,
context = app.buffer.context,
image = app.image,
mouse = app.mouse,
setInterval = app.win.setInterval;
/**
* The interval ID used for running the pencil drawing operation every few
* milliseconds.
*
* @private
* @see PaintWeb.config.toolDrawDelay
*/
var timer = null;
/**
* Holds the points needed to be drawn. Each point is added by the
* <code>mousemove</code> event handler.
*
* @private
* @type Array
*/
var points = [];
/**
* Holds the last point on the <var>x</var> axis of the image, for the current
* drawing operation.
*
* @private
* @type Number
*/
var x0 = 0;
/**
* Holds the last point on the <var>y</var> axis of the image, for the current
* drawing operation.
*
* @private
* @type Number
*/
var y0 = 0;
/**
* Tool deactivation event handler.
*/
this.deactivate = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
if (mouse.buttonDown) {
context.clearRect(0, 0, image.width, image.height);
}
points = [];
};
/**
* Initialize the drawing operation.
*/
this.mousedown = function () {
x0 = mouse.x;
y0 = mouse.y;
points = [];
if (!timer) {
timer = setInterval(_self.draw, app.config.toolDrawDelay);
}
return true;
};
/**
* Save the mouse coordinates in the array.
*/
this.mousemove = function () {
if (mouse.buttonDown) {
points.push(mouse.x, mouse.y);
}
};
/**
* Draw the points in the stack. This function is called every few
* milliseconds.
*
* @see PaintWeb.config.toolDrawDelay
*/
this.draw = function () {
var i = 0, n = points.length;
if (!n) {
return;
}
context.beginPath();
context.moveTo(x0, y0);
while (i < n) {
x0 = points[i++];
y0 = points[i++];
context.lineTo(x0, y0);
}
context.stroke();
context.closePath();
points = [];
};
/**
* End the drawing operation, once the user releases the mouse button.
*/
this.mouseup = function () {
if (mouse.x == x0 && mouse.y == y0) {
points.push(x0+1, y0+1);
}
if (timer) {
clearInterval(timer);
timer = null;
}
_self.draw();
app.layerUpdate();
return true;
};
/**
* Allows the user to press <kbd>Escape</kbd> to cancel the drawing operation.
*
* @param {Event} ev The DOM Event object.
*
* @returns {Boolean} True if the drawing operation was cancelled, or false if
* not.
*/
this.keydown = function (ev) {
if (!mouse.buttonDown || ev.kid_ != 'Escape') {
return false;
}
if (timer) {
clearInterval(timer);
timer = null;
}
context.clearRect(0, 0, image.width, image.height);
mouse.buttonDown = false;
points = [];
return true;
};
};
// vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save