diff --git a/.gitignore b/.gitignore index 9b9052bb..d0bf7593 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .tm_sync.config .idea +/node_modules/ +misc/cookies.txt \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..7be12653 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,72 @@ +'use strict'; + +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', + // Task configuration. + distFileName: '<%= pkg.name %>-<%= pkg.version %>', + concat: { + options: { + banner: '<%= banner %>', + stripBanners: true, + separator: ';' + }, + dist: { + src: [ + 'src/browser.js', + 'src/lang/*.js', + 'src/dom/*.js', + 'src/quirks/*.js', + 'src/selection/*.js', + 'src/commands.js', + 'src/commands/*.js', + 'src/undo_manager.js', + 'src/views/*.js', + 'src/toolbar/*.js', + 'src/editor.js' + ], + dest: 'dist/<%= distFileName %>.js' + }, + }, + uglify: { + options: '<%= concat.options %>', + dist: { + src: '<%= concat.dist.src %>', + dest: 'dist/<%= distFileName %>.min.js' + }, + }, + nodeunit: { + files: ['test/**/*_test.js'] + }, + watch: { + test: { + files: ['test/*.js', 'test/*/*.js', 'test/*.html'], + tasks: ['qunit'] + }, + }, + qunit: { + options: { + timeout: 10000, + '--cookies-file': 'misc/cookies.txt' + }, + all: ['test/*.html'] + } + }); + + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-nodeunit'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-qunit'); + + grunt.registerTask('build', ['concat', 'uglify']); + +}; diff --git a/Makefile b/Makefile deleted file mode 100644 index ca89896b..00000000 --- a/Makefile +++ /dev/null @@ -1,97 +0,0 @@ -VERSION = $(shell cat version.txt) - -JS_OUTPUT = "dist/wysihtml5-${VERSION}.js" - -OPEN = $(shell which xdg-open || which gnome-open || which open) - -JS_FILES = src/wysihtml5.js \ - lib/rangy/rangy-core.js \ - lib/base/base.js \ - src/browser.js \ - src/lang/array.js \ - src/lang/dispatcher.js \ - src/lang/object.js \ - src/lang/string.js \ - src/dom/auto_link.js \ - src/dom/class.js \ - src/dom/contains.js \ - src/dom/convert_to_list.js \ - src/dom/copy_attributes.js \ - src/dom/copy_styles.js \ - src/dom/delegate.js \ - src/dom/get_as_dom.js \ - src/dom/get_parent_element.js \ - src/dom/get_style.js \ - src/dom/has_element_with_tag_name.js \ - src/dom/has_element_with_class_name.js \ - src/dom/insert.js \ - src/dom/insert_css.js \ - src/dom/observe.js \ - src/dom/parse.js \ - src/dom/remove_empty_text_nodes.js \ - src/dom/rename_element.js \ - src/dom/replace_with_child_nodes.js \ - src/dom/resolve_list.js \ - src/dom/sandbox.js \ - src/dom/set_attributes.js \ - src/dom/set_styles.js \ - src/dom/simulate_placeholder.js \ - src/dom/text_content.js \ - src/quirks/clean_pasted_html.js \ - src/quirks/ensure_proper_clearing.js \ - src/quirks/get_correct_inner_html.js \ - src/quirks/redraw.js \ - src/selection/selection.js \ - src/selection/html_applier.js \ - src/commands.js \ - src/commands/bold.js \ - src/commands/createLink.js \ - src/commands/fontSize.js \ - src/commands/foreColor.js \ - src/commands/formatBlock.js \ - src/commands/formatInline.js \ - src/commands/insertHTML.js \ - src/commands/insertImage.js \ - src/commands/insertLineBreak.js \ - src/commands/insertOrderedList.js \ - src/commands/insertUnorderedList.js \ - src/commands/italic.js \ - src/commands/justifyCenter.js \ - src/commands/justifyLeft.js \ - src/commands/justifyRight.js \ - src/commands/justifyFull.js \ - src/commands/redo.js \ - src/commands/underline.js \ - src/commands/undo.js \ - src/undo_manager.js \ - src/views/view.js \ - src/views/composer.js \ - src/views/composer.style.js \ - src/views/composer.observe.js \ - src/views/synchronizer.js \ - src/views/textarea.js \ - src/toolbar/dialog.js \ - src/toolbar/speech.js \ - src/toolbar/toolbar.js \ - src/editor.js - -all: bundle minify - -bundle: - @@echo "Bundling..." - @@touch ${JS_OUTPUT} - @@rm ${JS_OUTPUT} - @@cat ${JS_FILES} >> ${JS_OUTPUT} - @@cat ${JS_OUTPUT} | sed "s/@VERSION/${VERSION}/" > "${JS_OUTPUT}.tmp" - @@mv "${JS_OUTPUT}.tmp" ${JS_OUTPUT} - -minify: - @@echo "Minifying... (this requires node.js)" - @@node build/minify.js ${JS_OUTPUT} - @@echo "Done." - -unittest: - @@${OPEN} test/index.html - -clean: - @@git co ${JS_OUTPUT} diff --git a/build/minify.js b/build/minify.js deleted file mode 100644 index c7d1b80e..00000000 --- a/build/minify.js +++ /dev/null @@ -1,82 +0,0 @@ -var script = process.argv[2], - http = require("http"), - queryString = require("querystring"), - fs = require("fs"); - -if (!script) { - throw "No script url given"; -} - -function post(code, callback) { - // Build the post string from an object - var postData = queryString.stringify({ - compilation_level: "SIMPLE_OPTIMIZATIONS", - output_format: "text", - output_info: "compiled_code", - warning_level: "QUIET", - js_code: code - }); - - // An object of options to indicate where to post to - var postOptions = { - host: "closure-compiler.appspot.com", - port: "80", - path: "/compile", - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - "Content-Length": postData.length - } - }; - - // Set up the request - var request = http.request(postOptions, function(response) { - var responseText = []; - response.setEncoding("utf8"); - response.on("data", function(data) { - responseText.push(data); - }); - response.on("end", function() { - callback(responseText.join("")); - }); - }); - - // Post the data - request.write(postData); - request.end(); -} - -function readFile(filePath, callback) { - // This is an async file read - fs.readFile(filePath, "utf-8", function (err, data) { - if (err) { - // If this were just a small part of the application, you would - // want to handle this differently, maybe throwing an exception - // for the caller to handle. Since the file is absolutely essential - // to the program's functionality, we're going to exit with a fatal - // error instead. - console.log("FATAL An error occurred trying to read in the file: " + err); - process.exit(-2); - } - // Make sure there's data before we post it - if (data) { - callback(data); - } else { - console.log("No data to post"); - process.exit(-1); - } - }); -} - -function writeFile(filePath, data, callback) { - fs.writeFile(filePath, data, "utf-8", callback); -} - - -// Ok GO! -readFile(script, function(code) { - post(code, function(code) { - var output = script.replace(/\.js/, ".min.js"); - writeFile(output, code); - }); -}); diff --git a/package.json b/package.json new file mode 100644 index 00000000..33e2dbfa --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "wysihtml5", + "description": "advanced wysihtml5", + "version": "0.5.0pre", + "homepage": "https://github.com/winsonwq/wysihtml5", + "author": { + "name": "Wang Qiu", + "email": "winsonwq@gmail.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/winsonwq/wysihtml5.git" + }, + "bugs": { + "url": "https://github.com/winsonwq/wysihtml5/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/winsonwq/wysihtml5/blob/master/LICENSE" + } + ], + "main": "lib/wysihtml5", + "engines": { + "node": ">= 0.6.0" + }, + "scripts": { + "test": "grunt nodeunit" + }, + "devDependencies": { + "grunt-contrib-qunit": "~0.2.1", + "grunt-contrib-concat": "~0.1.2", + "grunt-contrib-uglify": "~0.1.1", + "grunt-contrib-jshint": "~0.1.1", + "grunt-contrib-nodeunit": "~0.1.2", + "grunt-contrib-watch": "~0.2.0", + "grunt": "~0.4.1" + }, + "keywords": [] +} \ No newline at end of file diff --git a/test/dom/sandbox_test.js b/test/dom/sandbox_test.js index 9a636576..813324f6 100644 --- a/test/dom/sandbox_test.js +++ b/test/dom/sandbox_test.js @@ -148,7 +148,9 @@ asyncTest("Check insertion of single stylesheet", function() { new wysihtml5.dom.Sandbox(function(sandbox) { var doc = sandbox.getDocument(); equal(doc.getElementsByTagName("link").length, 1, "Correct amount of stylesheets inserted into the dom tree"); - start(); + setTimeout(function(){ + start(); + }, 2000); }, { stylesheets: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/blitzer/jquery-ui.css" }).insertInto(document.body); @@ -161,7 +163,9 @@ asyncTest("Check insertion of multiple stylesheets", function() { new wysihtml5.dom.Sandbox(function(sandbox) { var doc = sandbox.getDocument(); equal(doc.getElementsByTagName("link").length, 2, "Correct amount of stylesheets inserted into the dom tree"); - start(); + setTimeout(function(){ + start(); + }, 2000); }, { stylesheets: [ "https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/blitzer/jquery-ui.css", @@ -179,6 +183,8 @@ asyncTest("Check X-UA-Compatible", function() { docMode = doc.documentMode; ok(doc.documentMode === document.documentMode, "iFrame is in in the same document mode as the parent site"); - start(); + setTimeout(function() { + start(); + }, 2000); }).insertInto(document.body); }); \ No newline at end of file diff --git a/test/editor_test.js b/test/editor_test.js index dd1e0700..2e433367 100644 --- a/test/editor_test.js +++ b/test/editor_test.js @@ -1,4 +1,7 @@ if (wysihtml5.browser.supported()) { + + var originStart = start; + module("wysihtml5.Editor", { setup: function() { wysihtml5.dom.insertCSS([ @@ -11,14 +14,20 @@ if (wysihtml5.browser.supported()) { this.textareaElement.id = "wysihtml5-test-textarea"; this.textareaElement.title = "Please enter your foo"; this.textareaElement.value = "hey tiff, what's up?"; - + this.form = document.createElement("form"); this.form.onsubmit = function() { return false; }; this.form.appendChild(this.textareaElement); - + this.originalBodyClassName = document.body.className; - + document.body.appendChild(this.form); + + start = function() { + setTimeout(function() { + originStart(); + }, 2000); + }; }, teardown: function() { @@ -28,6 +37,8 @@ if (wysihtml5.browser.supported()) { } this.form.parentNode.removeChild(this.form); document.body.className = this.originalBodyClassName; + + start = originStart; }, getComposerElement: function() { @@ -39,12 +50,12 @@ if (wysihtml5.browser.supported()) { return iframes[iframes.length - 1]; } }); - + asyncTest("Basic test", function() { expect(18); - + var that = this; - + var editor = new wysihtml5.Editor(this.textareaElement); editor.on("load", function() { var iframeElement = that.getIframeElement(), @@ -55,7 +66,7 @@ if (wysihtml5.browser.supported()) { equal(textareaElement.style.display, "none", "Textarea not visible"); ok(iframeElement.style.display, "", "Editor iFrame is visible"); equal(editor.currentView.name, "composer", "Current view is 'composer'"); - + // Make textarea visible for a short amount of time, in order to calculate dimensions properly textareaElement.style.display = "block"; deepEqual( @@ -64,7 +75,7 @@ if (wysihtml5.browser.supported()) { "Editor has the same dimensions as the original textarea" ); textareaElement.style.display = "none"; - + var hiddenField = textareaElement.nextSibling; equal(hiddenField.name, "_wysihtml5_mode", "Hidden field has correct name"); equal(hiddenField.value, "1", "Hidden field has correct value"); @@ -76,15 +87,15 @@ if (wysihtml5.browser.supported()) { equal(wysihtml5.dom.getStyle("font-style").from(composerElement), "italic", "Correct font-style applied to editor element"); equal(wysihtml5.dom.getStyle("width").from(iframeElement), "50%", "Correct width applied to iframe"); equal(wysihtml5.dom.getStyle("height").from(iframeElement), "100px", "Correct height applied to iframe"); - + if ("borderRadius" in document.createElement("div").style) { expect(19); ok(wysihtml5.dom.getStyle("border-top-right-radius").from(iframeElement).indexOf("2px") !== -1, "border-radius correctly copied"); } - + equal(composerElement.innerHTML.toLowerCase(), "hey tiff, what's up?", "Copied the initial textarea value to the editor"); ok(wysihtml5.dom.hasClass(composerElement, "wysihtml5-editor"), "Editor element has correct class name"); - + start(); }); }); @@ -92,13 +103,13 @@ if (wysihtml5.browser.supported()) { asyncTest("Check setting of name as class name on iframe and iframe's body", function() { expect(4); - + this.textareaElement.className = "death-star"; - + var that = this, name = "star-wars-input", editor = new wysihtml5.Editor(this.textareaElement, { name: "star-wars-input" }); - + editor.on("load", function() { var iframeElement = that.getIframeElement(), composerElement = that.getComposerElement(), @@ -114,16 +125,16 @@ if (wysihtml5.browser.supported()) { asyncTest("Check textarea with box-sizing: border-box;", function() { expect(1); - + var that = this; - + wysihtml5.dom.setStyles({ MozBoxSizing: "border-box", WebkitBoxSizing: "border-box", MsBoxSizing: "border-box", boxSizing: "border-box" }).on(this.textareaElement); - + var editor = new wysihtml5.Editor(this.textareaElement); editor.on("load", function() { // Make textarea visible for a short amount of time, in order to calculate dimensions properly @@ -134,23 +145,23 @@ if (wysihtml5.browser.supported()) { "Editor has the same dimensions as the original textarea" ); that.textareaElement.style.display = "none"; - + start(); }); }); - + asyncTest("Check whether cols and rows attribute is correctly handled", function() { expect(2); - + var that = this; - + // Remove styles this.textareaElement.removeAttribute("id"); - + // And set dimensions of