라이브러리 추가(이미지편집기, 날짜 입력기)
@ -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]]
|
@ -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:
|
||||
|
@ -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 ' '),
|
||||
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');
|
||||
};
|
||||
}
|
||||
}());
|
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 625 B |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 784 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 656 B |
After Width: | Height: | Size: 634 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 827 B |
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 423 B |
After Width: | Height: | Size: 453 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 577 B |
After Width: | Height: | Size: 785 B |
After Width: | Height: | Size: 791 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 569 B |
After Width: | Height: | Size: 558 B |
After Width: | Height: | Size: 322 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 575 B |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 136 B |
After Width: | Height: | Size: 116 B |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 136 B |
After Width: | Height: | Size: 38 KiB |
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"> </li>
|
||||
|
||||
<li data-pwTool="selection">Selection</li>
|
||||
<!--<li data-pwTool="hand">Hand</li>-->
|
||||
<li data-pwTool="rectangle">Rectangle</li>
|
||||
|
||||
<li class="paintweb_toolSeparator"> </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"> </span>
|
||||
</p>
|
||||
<p class="paintweb_opt_strokeStyle">Stroke
|
||||
<span data-pwColorInput="strokeStyle"> </span>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li class="paintweb_toolSeparator"> </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"> </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">
|
||||
|
||||
<!– Elements with data-pwConfig will have class names like
|
||||
paintweb_cfg_configVariable. In this case the class name will be
|
||||
.paintweb_cfg_line_lineWidth. –>
|
||||
<p class="paintweb_opt_lineWidth"><label>Line width <input
|
||||
data-pwConfig="line.lineWidth" type="number" min="1" value="1"
|
||||
/></label></p>
|
||||
|
||||
<!– .paintweb_cfg_line_miterLimit (dot becomes underscore) –>
|
||||
<p class="paintweb_opt_miterLimit"><label>Miter limit <input
|
||||
data-pwConfig="line.miterLimit" type="number" min="1" value="10"
|
||||
/></label></p>
|
||||
|
||||
<!– .paintweb_cfg_line_lineCap –>
|
||||
<div data-pwConfig="line.lineCap">
|
||||
<p>Line cap</p>
|
||||
|
||||
<!– 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 –>
|
||||
<div data-pwConfigValue="butt">Butt</div>
|
||||
|
||||
<!– .paintweb_lineCap_square –>
|
||||
<div data-pwConfigValue="square">Square</div>
|
||||
<!– .paintweb_lineCap_round –>
|
||||
<div data-pwConfigValue="round">Round</div>
|
||||
</div>
|
||||
|
||||
<!– .paintweb_cfg_line_lineJoin –>
|
||||
<div data-pwConfig="line.lineJoin">
|
||||
<p>Line join</p>
|
||||
|
||||
<!– .paintweb_lineJoin_miter –>
|
||||
<div data-pwConfigValue="miter">Miter</div>
|
||||
<!– .paintweb_lineJoin_round –>
|
||||
<div data-pwConfigValue="round">Round</div>
|
||||
<!– .paintweb_lineJoin_bevel –>
|
||||
<div data-pwConfigValue="bevel">Bevel</div>
|
||||
</div>
|
||||
|
||||
<!– .paintweb_cfg_shapeType –>
|
||||
<div data-pwConfig="shapeType">
|
||||
<p>Shape type</p>
|
||||
|
||||
<!– .paintweb_shapeType_both –>
|
||||
<div data-pwConfigValue="both">Both</div>
|
||||
<!– .paintweb_shapeType_fill –>
|
||||
<div data-pwConfigValue="fill">Fill</div>
|
||||
<!– .paintweb_shapeType_stroke –>
|
||||
<div data-pwConfigValue="stroke">Stroke</div>
|
||||
</div>
|
||||
|
||||
<!– erm *cough* elements with data-pwConfigValue will be considered icons
|
||||
(class name .paintweb_icon is added). They will also have an anchor
|
||||
element appended. –>
|
||||
</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"> </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> </span> Active</li>
|
||||
<li id="colormixer_colorOld"><span> </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:
|
||||
}
|
@ -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:
|
||||
|