18315626215 5 年之前
父節點
當前提交
f4c8bf1040
共有 52 個文件被更改,包括 13911 次插入8 次删除
  1. 37 0
      .idea/php.xml
  2. 56 5
      .idea/workspace.xml
  3. 32 1
      .idea/youxiangjie.iml
  4. 31 0
      addons/simditor/Simditor.php
  5. 0 0
      addons/simditor/assets/css/simditor.min.css
  6. 二進制
      addons/simditor/assets/images/image.png
  7. 0 0
      addons/simditor/assets/js/simditor.min.js
  8. 48 0
      addons/simditor/bootstrap.js
  9. 4 0
      addons/simditor/build/build.sh
  10. 4 0
      addons/simditor/build/css.js
  11. 10 0
      addons/simditor/build/js.js
  12. 4699 0
      addons/simditor/build/r.js
  13. 4 0
      addons/simditor/config.php
  14. 8 0
      addons/simditor/info.ini
  15. 460 0
      addons/simditor/src/css/mobile.css
  16. 2 0
      addons/simditor/src/css/simditor.css
  17. 二進制
      addons/simditor/src/images/image.png
  18. 241 0
      addons/simditor/src/js/hotkeys.js
  19. 172 0
      addons/simditor/src/js/module.js
  20. 5641 0
      addons/simditor/src/js/simditor.js
  21. 261 0
      addons/simditor/src/js/uploader.js
  22. 181 0
      application/admin/controller/commodity/Commodity.php
  23. 76 0
      application/admin/controller/ctype/Ctype.php
  24. 40 0
      application/admin/controller/test/Test.php
  25. 0 1
      application/admin/controller/user/User.php
  26. 173 0
      application/admin/controller/users/Users.php
  27. 19 0
      application/admin/lang/zh-cn/commodity/commodity.php
  28. 6 0
      application/admin/lang/zh-cn/ctype/ctype.php
  29. 51 0
      application/admin/lang/zh-cn/test/test.php
  30. 16 0
      application/admin/lang/zh-cn/users/users.php
  31. 59 0
      application/admin/model/Command.php
  32. 150 0
      application/admin/model/test/Test.php
  33. 61 0
      application/admin/model/users/Users.php
  34. 95 0
      application/admin/view/commodity/commodity/add.html
  35. 96 0
      application/admin/view/commodity/commodity/edit.html
  36. 35 0
      application/admin/view/commodity/commodity/index.html
  37. 16 0
      application/admin/view/ctype/ctype/add.html
  38. 16 0
      application/admin/view/ctype/ctype/edit.html
  39. 35 0
      application/admin/view/ctype/ctype/index.html
  40. 240 0
      application/admin/view/test/test/add.html
  41. 240 0
      application/admin/view/test/test/edit.html
  42. 45 0
      application/admin/view/test/test/index.html
  43. 25 0
      application/admin/view/test/test/recyclebin.html
  44. 78 0
      application/admin/view/users/users/add.html
  45. 78 0
      application/admin/view/users/users/edit.html
  46. 35 0
      application/admin/view/users/users/index.html
  47. 86 0
      public/assets/js/backend/commodity/commodity.js
  48. 50 0
      public/assets/js/backend/ctype/ctype.js
  49. 136 0
      public/assets/js/backend/test/test.js
  50. 60 0
      public/assets/js/backend/users/users.js
  51. 2 1
      public/assets/js/require-table.js
  52. 1 0
      thinkphp/library/think/Model.php

+ 37 - 0
.idea/php.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="PhpIncludePathManager">
+    <include_path>
+      <path value="$PROJECT_DIR$/vendor/psr/container" />
+      <path value="$PROJECT_DIR$/vendor/psr/http-message" />
+      <path value="$PROJECT_DIR$/vendor/psr/log" />
+      <path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
+      <path value="$PROJECT_DIR$/vendor/pimple/pimple" />
+      <path value="$PROJECT_DIR$/vendor/endroid/qr-code" />
+      <path value="$PROJECT_DIR$/vendor/monolog/monolog" />
+      <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
+      <path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
+      <path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
+      <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php70" />
+      <path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
+      <path value="$PROJECT_DIR$/vendor/doctrine/cache" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/pinyin" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/socialite" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/wechat" />
+      <path value="$PROJECT_DIR$/vendor/topthink/think-captcha" />
+      <path value="$PROJECT_DIR$/vendor/topthink/think-installer" />
+      <path value="$PROJECT_DIR$/vendor/markbaker/complex" />
+      <path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
+      <path value="$PROJECT_DIR$/vendor/mtdowling/cron-expression" />
+      <path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
+      <path value="$PROJECT_DIR$/vendor/phpmailer/phpmailer" />
+      <path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
+      <path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
+      <path value="$PROJECT_DIR$/vendor/karsonzhang/fastadmin-addons" />
+      <path value="$PROJECT_DIR$/vendor/composer" />
+    </include_path>
+  </component>
+</project>

+ 56 - 5
.idea/workspace.xml

@@ -2,7 +2,12 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="0c798a10-12e9-4340-984e-11d11f343507" name="Default Changelist" comment="">
+      <change afterPath="$PROJECT_DIR$/.idea/php.xml" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/youxiangjie.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/youxiangjie.iml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/application/admin/controller/user/User.php" beforeDir="false" afterPath="$PROJECT_DIR$/application/admin/controller/user/User.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/public/assets/js/require-table.js" beforeDir="false" afterPath="$PROJECT_DIR$/public/assets/js/require-table.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/thinkphp/library/think/Model.php" beforeDir="false" afterPath="$PROJECT_DIR$/thinkphp/library/think/Model.php" afterDir="false" />
     </list>
     <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
     <option name="SHOW_DIALOG" value="false" />
@@ -10,7 +15,8 @@
     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
     <option name="LAST_RESOLUTION" value="IGNORE" />
   </component>
-  <component name="ComposerSettings">
+  <component name="ComposerSettings" doNotAsk="true" synchronizationState="SYNCHRONIZE">
+    <pharConfigPath>$PROJECT_DIR$/composer.json</pharConfigPath>
     <execution>
       <executable />
     </execution>
@@ -19,11 +25,45 @@
     <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
     <option name="UPDATE_TYPE" value="MERGE" />
   </component>
+  <component name="PhpWorkspaceProjectConfiguration">
+    <include_path>
+      <path value="$PROJECT_DIR$/vendor/psr/container" />
+      <path value="$PROJECT_DIR$/vendor/psr/http-message" />
+      <path value="$PROJECT_DIR$/vendor/psr/log" />
+      <path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
+      <path value="$PROJECT_DIR$/vendor/pimple/pimple" />
+      <path value="$PROJECT_DIR$/vendor/endroid/qr-code" />
+      <path value="$PROJECT_DIR$/vendor/monolog/monolog" />
+      <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
+      <path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
+      <path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
+      <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php70" />
+      <path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
+      <path value="$PROJECT_DIR$/vendor/doctrine/cache" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/pinyin" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/socialite" />
+      <path value="$PROJECT_DIR$/vendor/overtrue/wechat" />
+      <path value="$PROJECT_DIR$/vendor/topthink/think-captcha" />
+      <path value="$PROJECT_DIR$/vendor/topthink/think-installer" />
+      <path value="$PROJECT_DIR$/vendor/markbaker/complex" />
+      <path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
+      <path value="$PROJECT_DIR$/vendor/mtdowling/cron-expression" />
+      <path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
+      <path value="$PROJECT_DIR$/vendor/phpmailer/phpmailer" />
+      <path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
+      <path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
+      <path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
+      <path value="$PROJECT_DIR$/vendor/karsonzhang/fastadmin-addons" />
+      <path value="$PROJECT_DIR$/vendor/composer" />
+    </include_path>
+  </component>
   <component name="ProjectId" id="1Um3PwqsxizdOTLBw4mBJ8EzGbT" />
   <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
   <component name="PropertiesComponent">
     <property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
-    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="WebServerToolWindowFactoryState" value="true" />
     <property name="WebServerToolWindowPanel.toolwindow.highlight.mappings" value="true" />
     <property name="WebServerToolWindowPanel.toolwindow.highlight.symlinks" value="true" />
     <property name="WebServerToolWindowPanel.toolwindow.show.date" value="false" />
@@ -59,7 +99,9 @@
       <option name="number" value="Default" />
       <option name="presentableId" value="Default" />
       <updated>1575947922294</updated>
-      <workItem from="1575947923604" duration="6007000" />
+      <workItem from="1575947923604" duration="10146000" />
+      <workItem from="1576024818220" duration="20064000" />
+      <workItem from="1576110627926" duration="628000" />
     </task>
     <task id="LOCAL-00001" summary="提交测试">
       <created>1575956432691</created>
@@ -82,7 +124,14 @@
       <option name="project" value="LOCAL" />
       <updated>1575961132174</updated>
     </task>
-    <option name="localTasksCounter" value="4" />
+    <task id="LOCAL-00004" summary="配置">
+      <created>1575962376579</created>
+      <option name="number" value="00004" />
+      <option name="presentableId" value="LOCAL-00004" />
+      <option name="project" value="LOCAL" />
+      <updated>1575962376579</updated>
+    </task>
+    <option name="localTasksCounter" value="5" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">
@@ -93,7 +142,9 @@
       <map>
         <entry key="MAIN">
           <value>
-            <State />
+            <State>
+              <option name="COLUMN_ORDER" />
+            </State>
           </value>
         </entry>
       </map>

+ 32 - 1
.idea/youxiangjie.iml

@@ -1,7 +1,38 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="WEB_MODULE" version="4">
   <component name="NewModuleRootManager">
-    <content url="file://$MODULE_DIR$" />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/endroid/qr-code" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/karsonzhang/fastadmin-addons" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/mtdowling/cron-expression" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/pinyin" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/socialite" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/wechat" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/phpmailer/phpmailer" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/pimple/pimple" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php70" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/psr-http-message-bridge" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-captcha" />
+      <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-installer" />
+    </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>

+ 31 - 0
addons/simditor/Simditor.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace addons\simditor;
+
+use think\Addons;
+
+/**
+ * 插件
+ */
+class Simditor extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+        return true;
+    }
+
+}

文件差異過大導致無法顯示
+ 0 - 0
addons/simditor/assets/css/simditor.min.css


二進制
addons/simditor/assets/images/image.png


文件差異過大導致無法顯示
+ 0 - 0
addons/simditor/assets/js/simditor.min.js


+ 48 - 0
addons/simditor/bootstrap.js

@@ -0,0 +1,48 @@
+require.config({
+    paths: {
+        'simditor': '../addons/simditor/js/simditor.min',
+    },
+    shim: {
+        'simditor': [
+            'css!../addons/simditor/css/simditor.min.css'
+        ]
+    }
+});
+require(['form'], function (Form) {
+    var _bindevent = Form.events.bindevent;
+    Form.events.bindevent = function (form) {
+        _bindevent.apply(this, [form]);
+        if ($(".editor", form).size() > 0) {
+            //修改上传的接口调用
+            require(['upload', 'simditor'], function (Upload, Simditor) {
+                var editor, mobileToolbar, toolbar;
+                Simditor.locale = 'zh-CN';
+                Simditor.list = {};
+                toolbar = ['title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link', 'image', 'hr', '|', 'indent', 'outdent', 'alignment'];
+                mobileToolbar = ["bold", "underline", "strikethrough", "color", "ul", "ol"];
+                $(".editor", form).each(function () {
+                    var id = $(this).attr("id");
+                    editor = new Simditor({
+                        textarea: this,
+                        toolbarFloat: false,
+                        toolbar: toolbar,
+                        pasteImage: true,
+                        defaultImage: Config.__CDN__ + '/assets/addons/simditor/images/image.png',
+                        upload: {url: '/'}
+                    });
+                    editor.uploader.on('beforeupload', function (e, file) {
+                        Upload.api.send(file.obj, function (data) {
+                            var url = Fast.api.cdnurl(data.url);
+                            editor.uploader.trigger("uploadsuccess", [file, {success: true, file_path: url}]);
+                        });
+                        return false;
+                    });
+                    editor.on("blur", function () {
+                        this.textarea.trigger("blur");
+                    });
+                    Simditor.list[id] = editor;
+                });
+            });
+        }
+    }
+});

+ 4 - 0
addons/simditor/build/build.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+/usr/local/bin/node r.js -o ./js.js name=simditor baseUrl=../src/js out=../assets/js/simditor.min.js
+/usr/local/bin/node r.js -o ./css.js cssIn=../src/css/simditor.css out=../assets/css/simditor.min.css

+ 4 - 0
addons/simditor/build/css.js

@@ -0,0 +1,4 @@
+({
+  optimizeCss: "default",
+  optimize: "uglify"
+})

+ 10 - 0
addons/simditor/build/js.js

@@ -0,0 +1,10 @@
+({
+    name: "simditor",
+    paths: {
+        'jquery': 'empty:',
+        'simditor': 'simditor',
+        'simple-module': 'module',
+        'simple-uploader': 'uploader',
+        'simple-hotkeys': 'hotkeys',
+    },
+});

文件差異過大導致無法顯示
+ 4699 - 0
addons/simditor/build/r.js


+ 4 - 0
addons/simditor/config.php

@@ -0,0 +1,4 @@
+<?php
+
+return [
+];

+ 8 - 0
addons/simditor/info.ini

@@ -0,0 +1,8 @@
+name = simditor
+title = Simditor
+intro = 简洁清晰的富文本插件
+author = Karson
+website = http://www.fastadmin.net
+version = 1.0.5
+state = 1
+url = /addons/simditor

+ 460 - 0
addons/simditor/src/css/mobile.css

@@ -0,0 +1,460 @@
+@media screen and (max-device-width: 240px) and (min-device-width: 220px) {
+  body {
+    width: 240px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 320px) and (min-device-width: 300px) {
+  body {
+    width: 320px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 360px) and (min-device-width: 340px) {
+  body {
+    width: 360px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 480px) and (min-device-width: 460px) {
+  body {
+    width: 480px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 640px) and (min-device-width: 620px) {
+  body {
+    width: 320px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 720px) and (min-device-width: 700px) {
+  body {
+    width: 360px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 80px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 80px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 800px) and (min-device-width: 780px) {
+  body {
+    width: 400px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 88.8888888889px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 88.8888888889px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 960px) and (min-device-width: 940px) {
+  body {
+    width: 480px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 100px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 100px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 1024px) and (min-device-width: 1004px) {
+  body {
+    width: 512px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 100px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 100px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (max-device-width: 1280px) and (min-device-width: 1260px) {
+  body {
+    width: 640px;
+    margin: 0 auto;
+  }
+  body .wrapper {
+    width: 100%;
+  }
+  body .wrapper header {
+    padding: 30px 0 20px;
+  }
+  body .wrapper header h1 {
+    background-size: 200px auto;
+    background-position: 50px 0;
+    padding-top: 90px;
+    height: 45px;
+  }
+  body .wrapper header h1 a {
+    background-size: 160px auto;
+    background-position: 10px 0;
+  }
+  body .wrapper header p.desc {
+    font-size: 16px;
+  }
+  body .wrapper footer {
+    margin: 20px 0;
+  }
+  body .wrapper #page-demo {
+    width: 96%;
+    margin: 0 2%;
+  }
+  body .wrapper #link-fork {
+    z-index: -1;
+    width: 100px;
+    height: auto;
+  }
+  body .wrapper #link-fork img {
+    max-width: 100px;
+    height: auto;
+  }
+
+  nav {
+    display: none;
+  }
+}
+@media screen and (device-aspect-ratio: 40 / 71) and (orientation: landscape) {
+  body {
+    width: 568px;
+  }
+}
+@media screen and (device-aspect-ratio: 2 / 3) and (orientation: landscape) {
+  body {
+    width: 480px;
+  }
+}

文件差異過大導致無法顯示
+ 2 - 0
addons/simditor/src/css/simditor.css


二進制
addons/simditor/src/images/image.png


+ 241 - 0
addons/simditor/src/js/hotkeys.js

@@ -0,0 +1,241 @@
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module unless amdModuleId is set
+    define('simple-hotkeys', ["jquery","simple-module"], function ($, SimpleModule) {
+      return (root['hotkeys'] = factory($, SimpleModule));
+    });
+  } else if (typeof exports === 'object') {
+    // Node. Does not work with strict CommonJS, but
+    // only CommonJS-like environments that support module.exports,
+    // like Node.
+    module.exports = factory(require("jquery"),require("simple-module"));
+  } else {
+    root.simple = root.simple || {};
+    root.simple['hotkeys'] = factory(jQuery,SimpleModule);
+  }
+}(this, function ($, SimpleModule) {
+
+var Hotkeys, hotkeys,
+  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+  hasProp = {}.hasOwnProperty;
+
+Hotkeys = (function(superClass) {
+  extend(Hotkeys, superClass);
+
+  function Hotkeys() {
+    return Hotkeys.__super__.constructor.apply(this, arguments);
+  }
+
+  Hotkeys.count = 0;
+
+  Hotkeys.keyNameMap = {
+    8: "Backspace",
+    9: "Tab",
+    13: "Enter",
+    16: "Shift",
+    17: "Control",
+    18: "Alt",
+    19: "Pause",
+    20: "CapsLock",
+    27: "Esc",
+    32: "Spacebar",
+    33: "PageUp",
+    34: "PageDown",
+    35: "End",
+    36: "Home",
+    37: "Left",
+    38: "Up",
+    39: "Right",
+    40: "Down",
+    45: "Insert",
+    46: "Del",
+    91: "Meta",
+    93: "Meta",
+    48: "0",
+    49: "1",
+    50: "2",
+    51: "3",
+    52: "4",
+    53: "5",
+    54: "6",
+    55: "7",
+    56: "8",
+    57: "9",
+    65: "A",
+    66: "B",
+    67: "C",
+    68: "D",
+    69: "E",
+    70: "F",
+    71: "G",
+    72: "H",
+    73: "I",
+    74: "J",
+    75: "K",
+    76: "L",
+    77: "M",
+    78: "N",
+    79: "O",
+    80: "P",
+    81: "Q",
+    82: "R",
+    83: "S",
+    84: "T",
+    85: "U",
+    86: "V",
+    87: "W",
+    88: "X",
+    89: "Y",
+    90: "Z",
+    96: "0",
+    97: "1",
+    98: "2",
+    99: "3",
+    100: "4",
+    101: "5",
+    102: "6",
+    103: "7",
+    104: "8",
+    105: "9",
+    106: "Multiply",
+    107: "Add",
+    109: "Subtract",
+    110: "Decimal",
+    111: "Divide",
+    112: "F1",
+    113: "F2",
+    114: "F3",
+    115: "F4",
+    116: "F5",
+    117: "F6",
+    118: "F7",
+    119: "F8",
+    120: "F9",
+    121: "F10",
+    122: "F11",
+    123: "F12",
+    124: "F13",
+    125: "F14",
+    126: "F15",
+    127: "F16",
+    128: "F17",
+    129: "F18",
+    130: "F19",
+    131: "F20",
+    132: "F21",
+    133: "F22",
+    134: "F23",
+    135: "F24",
+    59: ";",
+    61: "=",
+    186: ";",
+    187: "=",
+    188: ",",
+    190: ".",
+    191: "/",
+    192: "`",
+    219: "[",
+    220: "\\",
+    221: "]",
+    222: "'"
+  };
+
+  Hotkeys.aliases = {
+    "escape": "esc",
+    "delete": "del",
+    "return": "enter",
+    "ctrl": "control",
+    "space": "spacebar",
+    "ins": "insert",
+    "cmd": "meta",
+    "command": "meta",
+    "wins": "meta",
+    "windows": "meta"
+  };
+
+  Hotkeys.normalize = function(shortcut) {
+    var i, j, key, keyname, keys, len;
+    keys = shortcut.toLowerCase().replace(/\s+/gi, "").split("+");
+    for (i = j = 0, len = keys.length; j < len; i = ++j) {
+      key = keys[i];
+      keys[i] = this.aliases[key] || key;
+    }
+    keyname = keys.pop();
+    keys.sort().push(keyname);
+    return keys.join("_");
+  };
+
+  Hotkeys.prototype.opts = {
+    el: document
+  };
+
+  Hotkeys.prototype._init = function() {
+    this.id = ++this.constructor.count;
+    this._map = {};
+    this._delegate = typeof this.opts.el === "string" ? document : this.opts.el;
+    return $(this._delegate).on("keydown.simple-hotkeys-" + this.id, this.opts.el, (function(_this) {
+      return function(e) {
+        var ref;
+        return (ref = _this._getHander(e)) != null ? ref.call(_this, e) : void 0;
+      };
+    })(this));
+  };
+
+  Hotkeys.prototype._getHander = function(e) {
+    var keyname, shortcut;
+    if (!(keyname = this.constructor.keyNameMap[e.which])) {
+      return;
+    }
+    shortcut = "";
+    if (e.altKey) {
+      shortcut += "alt_";
+    }
+    if (e.ctrlKey) {
+      shortcut += "control_";
+    }
+    if (e.metaKey) {
+      shortcut += "meta_";
+    }
+    if (e.shiftKey) {
+      shortcut += "shift_";
+    }
+    shortcut += keyname.toLowerCase();
+    return this._map[shortcut];
+  };
+
+  Hotkeys.prototype.respondTo = function(subject) {
+    if (typeof subject === 'string') {
+      return this._map[this.constructor.normalize(subject)] != null;
+    } else {
+      return this._getHander(subject) != null;
+    }
+  };
+
+  Hotkeys.prototype.add = function(shortcut, handler) {
+    this._map[this.constructor.normalize(shortcut)] = handler;
+    return this;
+  };
+
+  Hotkeys.prototype.remove = function(shortcut) {
+    delete this._map[this.constructor.normalize(shortcut)];
+    return this;
+  };
+
+  Hotkeys.prototype.destroy = function() {
+    $(this._delegate).off(".simple-hotkeys-" + this.id);
+    this._map = {};
+    return this;
+  };
+
+  return Hotkeys;
+
+})(SimpleModule);
+
+hotkeys = function(opts) {
+  return new Hotkeys(opts);
+};
+
+return hotkeys;
+
+}));
+

+ 172 - 0
addons/simditor/src/js/module.js

@@ -0,0 +1,172 @@
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module unless amdModuleId is set
+    define('simple-module', ["jquery"], function (a0) {
+      return (root['Module'] = factory(a0));
+    });
+  } else if (typeof exports === 'object') {
+    // Node. Does not work with strict CommonJS, but
+    // only CommonJS-like environments that support module.exports,
+    // like Node.
+    module.exports = factory(require("jquery"));
+  } else {
+    root['SimpleModule'] = factory(jQuery);
+  }
+}(this, function ($) {
+
+var Module,
+  slice = [].slice;
+
+Module = (function() {
+  Module.extend = function(obj) {
+    var key, ref, val;
+    if (!((obj != null) && typeof obj === 'object')) {
+      return;
+    }
+    for (key in obj) {
+      val = obj[key];
+      if (key !== 'included' && key !== 'extended') {
+        this[key] = val;
+      }
+    }
+    return (ref = obj.extended) != null ? ref.call(this) : void 0;
+  };
+
+  Module.include = function(obj) {
+    var key, ref, val;
+    if (!((obj != null) && typeof obj === 'object')) {
+      return;
+    }
+    for (key in obj) {
+      val = obj[key];
+      if (key !== 'included' && key !== 'extended') {
+        this.prototype[key] = val;
+      }
+    }
+    return (ref = obj.included) != null ? ref.call(this) : void 0;
+  };
+
+  Module.connect = function(cls) {
+    if (typeof cls !== 'function') {
+      return;
+    }
+    if (!cls.pluginName) {
+      throw new Error('Module.connect: cannot connect plugin without pluginName');
+      return;
+    }
+    cls.prototype._connected = true;
+    if (!this._connectedClasses) {
+      this._connectedClasses = [];
+    }
+    this._connectedClasses.push(cls);
+    if (cls.pluginName) {
+      return this[cls.pluginName] = cls;
+    }
+  };
+
+  Module.prototype.opts = {};
+
+  function Module(opts) {
+    var base, cls, i, instance, instances, len, name;
+    this.opts = $.extend({}, this.opts, opts);
+    (base = this.constructor)._connectedClasses || (base._connectedClasses = []);
+    instances = (function() {
+      var i, len, ref, results;
+      ref = this.constructor._connectedClasses;
+      results = [];
+      for (i = 0, len = ref.length; i < len; i++) {
+        cls = ref[i];
+        name = cls.pluginName.charAt(0).toLowerCase() + cls.pluginName.slice(1);
+        if (cls.prototype._connected) {
+          cls.prototype._module = this;
+        }
+        results.push(this[name] = new cls());
+      }
+      return results;
+    }).call(this);
+    if (this._connected) {
+      this.opts = $.extend({}, this.opts, this._module.opts);
+    } else {
+      this._init();
+      for (i = 0, len = instances.length; i < len; i++) {
+        instance = instances[i];
+        if (typeof instance._init === "function") {
+          instance._init();
+        }
+      }
+    }
+    this.trigger('initialized');
+  }
+
+  Module.prototype._init = function() {};
+
+  Module.prototype.on = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    (ref = $(this)).on.apply(ref, args);
+    return this;
+  };
+
+  Module.prototype.one = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    (ref = $(this)).one.apply(ref, args);
+    return this;
+  };
+
+  Module.prototype.off = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    (ref = $(this)).off.apply(ref, args);
+    return this;
+  };
+
+  Module.prototype.trigger = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    (ref = $(this)).trigger.apply(ref, args);
+    return this;
+  };
+
+  Module.prototype.triggerHandler = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    return (ref = $(this)).triggerHandler.apply(ref, args);
+  };
+
+  Module.prototype._t = function() {
+    var args, ref;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    return (ref = this.constructor)._t.apply(ref, args);
+  };
+
+  Module._t = function() {
+    var args, key, ref, result;
+    key = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
+    result = ((ref = this.i18n[this.locale]) != null ? ref[key] : void 0) || '';
+    if (!(args.length > 0)) {
+      return result;
+    }
+    result = result.replace(/([^%]|^)%(?:(\d+)\$)?s/g, function(p0, p, position) {
+      if (position) {
+        return p + args[parseInt(position) - 1];
+      } else {
+        return p + args.shift();
+      }
+    });
+    return result.replace(/%%s/g, '%s');
+  };
+
+  Module.i18n = {
+    'zh-CN': {}
+  };
+
+  Module.locale = 'zh-CN';
+
+  return Module;
+
+})();
+
+return Module;
+
+}));

+ 5641 - 0
addons/simditor/src/js/simditor.js

@@ -0,0 +1,5641 @@
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module unless amdModuleId is set
+    define('simditor', ["jquery","simple-module","simple-hotkeys","simple-uploader"], function ($, SimpleModule, simpleHotkeys, simpleUploader) {
+      return (root['Simditor'] = factory($, SimpleModule, simpleHotkeys, simpleUploader));
+    });
+  } else if (typeof exports === 'object') {
+    // Node. Does not work with strict CommonJS, but
+    // only CommonJS-like environments that support module.exports,
+    // like Node.
+    module.exports = factory(require("jquery"),require("simple-module"),require("simple-hotkeys"),require("simple-uploader"));
+  } else {
+    root['Simditor'] = factory(jQuery,SimpleModule,simple.hotkeys,simple.uploader);
+  }
+}(this, function ($, SimpleModule, simpleHotkeys, simpleUploader) {
+
+var AlignmentButton, BlockquoteButton, BoldButton, Button, Clipboard, CodeButton, CodePopover, ColorButton, FontScaleButton, Formatter, HrButton, ImageButton, ImagePopover, IndentButton, Indentation, InputManager, ItalicButton, Keystroke, LinkButton, LinkPopover, ListButton, OrderListButton, OutdentButton, Popover, Selection, Simditor, StrikethroughButton, TableButton, TitleButton, Toolbar, UnderlineButton, UndoManager, UnorderListButton, Util,
+  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+  hasProp = {}.hasOwnProperty,
+  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+  slice = [].slice;
+
+Selection = (function(superClass) {
+  extend(Selection, superClass);
+
+  function Selection() {
+    return Selection.__super__.constructor.apply(this, arguments);
+  }
+
+  Selection.pluginName = 'Selection';
+
+  Selection.prototype._range = null;
+
+  Selection.prototype._startNodes = null;
+
+  Selection.prototype._endNodes = null;
+
+  Selection.prototype._containerNode = null;
+
+  Selection.prototype._nodes = null;
+
+  Selection.prototype._blockNodes = null;
+
+  Selection.prototype._rootNodes = null;
+
+  Selection.prototype._init = function() {
+    this.editor = this._module;
+    this._selection = document.getSelection();
+    this.editor.on('selectionchanged', (function(_this) {
+      return function(e) {
+        _this.reset();
+        return _this._range = _this._selection.getRangeAt(0);
+      };
+    })(this));
+    this.editor.on('blur', (function(_this) {
+      return function(e) {
+        return _this.reset();
+      };
+    })(this));
+    return this.editor.on('focus', (function(_this) {
+      return function(e) {
+        _this.reset();
+        return _this._range = _this._selection.getRangeAt(0);
+      };
+    })(this));
+  };
+
+  Selection.prototype.reset = function() {
+    this._range = null;
+    this._startNodes = null;
+    this._endNodes = null;
+    this._containerNode = null;
+    this._nodes = null;
+    this._blockNodes = null;
+    return this._rootNodes = null;
+  };
+
+  Selection.prototype.clear = function() {
+    var e;
+    try {
+      this._selection.removeAllRanges();
+    } catch (_error) {
+      e = _error;
+    }
+    return this.reset();
+  };
+
+  Selection.prototype.range = function(range) {
+    var ffOrIE;
+    if (range) {
+      this.clear();
+      this._selection.addRange(range);
+      this._range = range;
+      ffOrIE = this.editor.util.browser.firefox || this.editor.util.browser.msie;
+      if (!this.editor.inputManager.focused && ffOrIE) {
+        this.editor.body.focus();
+      }
+    } else if (!this._range && this.editor.inputManager.focused && this._selection.rangeCount) {
+      this._range = this._selection.getRangeAt(0);
+    }
+    return this._range;
+  };
+
+  Selection.prototype.startNodes = function() {
+    if (this._range) {
+      this._startNodes || (this._startNodes = (function(_this) {
+        return function() {
+          var startNodes;
+          startNodes = $(_this._range.startContainer).parentsUntil(_this.editor.body).get();
+          startNodes.unshift(_this._range.startContainer);
+          return $(startNodes);
+        };
+      })(this)());
+    }
+    return this._startNodes;
+  };
+
+  Selection.prototype.endNodes = function() {
+    var endNodes;
+    if (this._range) {
+      this._endNodes || (this._endNodes = this._range.collapsed ? this.startNodes() : (endNodes = $(this._range.endContainer).parentsUntil(this.editor.body).get(), endNodes.unshift(this._range.endContainer), $(endNodes)));
+    }
+    return this._endNodes;
+  };
+
+  Selection.prototype.containerNode = function() {
+    if (this._range) {
+      this._containerNode || (this._containerNode = $(this._range.commonAncestorContainer));
+    }
+    return this._containerNode;
+  };
+
+  Selection.prototype.nodes = function() {
+    if (this._range) {
+      this._nodes || (this._nodes = (function(_this) {
+        return function() {
+          var nodes;
+          nodes = [];
+          if (_this.startNodes().first().is(_this.endNodes().first())) {
+            nodes = _this.startNodes().get();
+          } else {
+            _this.startNodes().each(function(i, node) {
+              var $endNode, $node, $nodes, endIndex, index, sharedIndex, startIndex;
+              $node = $(node);
+              if (_this.endNodes().index($node) > -1) {
+                return nodes.push(node);
+              } else if ($node.parent().is(_this.editor.body) || (sharedIndex = _this.endNodes().index($node.parent())) > -1) {
+                if (sharedIndex && sharedIndex > -1) {
+                  $endNode = _this.endNodes().eq(sharedIndex - 1);
+                } else {
+                  $endNode = _this.endNodes().last();
+                }
+                $nodes = $node.parent().contents();
+                startIndex = $nodes.index($node);
+                endIndex = $nodes.index($endNode);
+                return $.merge(nodes, $nodes.slice(startIndex, endIndex).get());
+              } else {
+                $nodes = $node.parent().contents();
+                index = $nodes.index($node);
+                return $.merge(nodes, $nodes.slice(index).get());
+              }
+            });
+            _this.endNodes().each(function(i, node) {
+              var $node, $nodes, index;
+              $node = $(node);
+              if ($node.parent().is(_this.editor.body) || _this.startNodes().index($node.parent()) > -1) {
+                nodes.push(node);
+                return false;
+              } else {
+                $nodes = $node.parent().contents();
+                index = $nodes.index($node);
+                return $.merge(nodes, $nodes.slice(0, index + 1));
+              }
+            });
+          }
+          return $($.unique(nodes));
+        };
+      })(this)());
+    }
+    return this._nodes;
+  };
+
+  Selection.prototype.blockNodes = function() {
+    if (!this._range) {
+      return;
+    }
+    this._blockNodes || (this._blockNodes = (function(_this) {
+      return function() {
+        return _this.nodes().filter(function(i, node) {
+          return _this.editor.util.isBlockNode(node);
+        });
+      };
+    })(this)());
+    return this._blockNodes;
+  };
+
+  Selection.prototype.rootNodes = function() {
+    if (!this._range) {
+      return;
+    }
+    this._rootNodes || (this._rootNodes = (function(_this) {
+      return function() {
+        return _this.nodes().filter(function(i, node) {
+          var $parent;
+          $parent = $(node).parent();
+          return $parent.is(_this.editor.body) || $parent.is('blockquote');
+        });
+      };
+    })(this)());
+    return this._rootNodes;
+  };
+
+  Selection.prototype.rangeAtEndOf = function(node, range) {
+    var afterLastNode, beforeLastNode, endNode, endNodeLength, lastNodeIsBr, result;
+    if (range == null) {
+      range = this.range();
+    }
+    if (!(range && range.collapsed)) {
+      return;
+    }
+    node = $(node)[0];
+    endNode = range.endContainer;
+    endNodeLength = this.editor.util.getNodeLength(endNode);
+    beforeLastNode = range.endOffset === endNodeLength - 1;
+    lastNodeIsBr = $(endNode).contents().last().is('br');
+    afterLastNode = range.endOffset === endNodeLength;
+    if (!((beforeLastNode && lastNodeIsBr) || afterLastNode)) {
+      return false;
+    }
+    if (node === endNode) {
+      return true;
+    } else if (!$.contains(node, endNode)) {
+      return false;
+    }
+    result = true;
+    $(endNode).parentsUntil(node).addBack().each(function(i, n) {
+      var $lastChild, beforeLastbr, isLastNode, nodes;
+      nodes = $(n).parent().contents().filter(function() {
+        return !(this !== n && this.nodeType === 3 && !this.nodeValue);
+      });
+      $lastChild = nodes.last();
+      isLastNode = $lastChild.get(0) === n;
+      beforeLastbr = $lastChild.is('br') && $lastChild.prev().get(0) === n;
+      if (!(isLastNode || beforeLastbr)) {
+        result = false;
+        return false;
+      }
+    });
+    return result;
+  };
+
+  Selection.prototype.rangeAtStartOf = function(node, range) {
+    var result, startNode;
+    if (range == null) {
+      range = this.range();
+    }
+    if (!(range && range.collapsed)) {
+      return;
+    }
+    node = $(node)[0];
+    startNode = range.startContainer;
+    if (range.startOffset !== 0) {
+      return false;
+    }
+    if (node === startNode) {
+      return true;
+    } else if (!$.contains(node, startNode)) {
+      return false;
+    }
+    result = true;
+    $(startNode).parentsUntil(node).addBack().each(function(i, n) {
+      var nodes;
+      nodes = $(n).parent().contents().filter(function() {
+        return !(this !== n && this.nodeType === 3 && !this.nodeValue);
+      });
+      if (nodes.first().get(0) !== n) {
+        return result = false;
+      }
+    });
+    return result;
+  };
+
+  Selection.prototype.insertNode = function(node, range) {
+    if (range == null) {
+      range = this.range();
+    }
+    if (!range) {
+      return;
+    }
+    node = $(node)[0];
+    range.insertNode(node);
+    return this.setRangeAfter(node, range);
+  };
+
+  Selection.prototype.setRangeAfter = function(node, range) {
+    if (range == null) {
+      range = this.range();
+    }
+    if (range == null) {
+      return;
+    }
+    node = $(node)[0];
+    range.setEndAfter(node);
+    range.collapse(false);
+    return this.range(range);
+  };
+
+  Selection.prototype.setRangeBefore = function(node, range) {
+    if (range == null) {
+      range = this.range();
+    }
+    if (range == null) {
+      return;
+    }
+    node = $(node)[0];
+    range.setEndBefore(node);
+    range.collapse(false);
+    return this.range(range);
+  };
+
+  Selection.prototype.setRangeAtStartOf = function(node, range) {
+    if (range == null) {
+      range = this.range();
+    }
+    node = $(node).get(0);
+    range.setEnd(node, 0);
+    range.collapse(false);
+    return this.range(range);
+  };
+
+  Selection.prototype.setRangeAtEndOf = function(node, range) {
+    var $lastNode, $node, contents, lastChild, lastChildLength, lastText, nodeLength;
+    if (range == null) {
+      range = this.range();
+    }
+    $node = $(node);
+    node = $node[0];
+    if ($node.is('pre')) {
+      contents = $node.contents();
+      if (contents.length > 0) {
+        lastChild = contents.last();
+        lastText = lastChild.text();
+        lastChildLength = this.editor.util.getNodeLength(lastChild[0]);
+        if (lastText.charAt(lastText.length - 1) === '\n') {
+          range.setEnd(lastChild[0], lastChildLength - 1);
+        } else {
+          range.setEnd(lastChild[0], lastChildLength);
+        }
+      } else {
+        range.setEnd(node, 0);
+      }
+    } else {
+      nodeLength = this.editor.util.getNodeLength(node);
+      if (node.nodeType !== 3 && nodeLength > 0) {
+        $lastNode = $(node).contents().last();
+        if ($lastNode.is('br')) {
+          nodeLength -= 1;
+        } else if ($lastNode[0].nodeType !== 3 && this.editor.util.isEmptyNode($lastNode)) {
+          $lastNode.append(this.editor.util.phBr);
+          node = $lastNode[0];
+          nodeLength = 0;
+        }
+      }
+      range.setEnd(node, nodeLength);
+    }
+    range.collapse(false);
+    return this.range(range);
+  };
+
+  Selection.prototype.deleteRangeContents = function(range) {
+    var atEndOfBody, atStartOfBody, endRange, startRange;
+    if (range == null) {
+      range = this.range();
+    }
+    startRange = range.cloneRange();
+    endRange = range.cloneRange();
+    startRange.collapse(true);
+    endRange.collapse(false);
+    atStartOfBody = this.rangeAtStartOf(this.editor.body, startRange);
+    atEndOfBody = this.rangeAtEndOf(this.editor.body, endRange);
+    if (!range.collapsed && atStartOfBody && atEndOfBody) {
+      this.editor.body.empty();
+      range.setStart(this.editor.body[0], 0);
+      range.collapse(true);
+      this.range(range);
+    } else {
+      range.deleteContents();
+    }
+    return range;
+  };
+
+  Selection.prototype.breakBlockEl = function(el, range) {
+    var $el;
+    if (range == null) {
+      range = this.range();
+    }
+    $el = $(el);
+    if (!range.collapsed) {
+      return $el;
+    }
+    range.setStartBefore($el.get(0));
+    if (range.collapsed) {
+      return $el;
+    }
+    return $el.before(range.extractContents());
+  };
+
+  Selection.prototype.save = function(range) {
+    var endCaret, endRange, startCaret;
+    if (range == null) {
+      range = this.range();
+    }
+    if (this._selectionSaved) {
+      return;
+    }
+    endRange = range.cloneRange();
+    endRange.collapse(false);
+    startCaret = $('<span/>').addClass('simditor-caret-start');
+    endCaret = $('<span/>').addClass('simditor-caret-end');
+    endRange.insertNode(endCaret[0]);
+    range.insertNode(startCaret[0]);
+    this.clear();
+    return this._selectionSaved = true;
+  };
+
+  Selection.prototype.restore = function() {
+    var endCaret, endContainer, endOffset, range, startCaret, startContainer, startOffset;
+    if (!this._selectionSaved) {
+      return false;
+    }
+    startCaret = this.editor.body.find('.simditor-caret-start');
+    endCaret = this.editor.body.find('.simditor-caret-end');
+    if (startCaret.length && endCaret.length) {
+      startContainer = startCaret.parent();
+      startOffset = startContainer.contents().index(startCaret);
+      endContainer = endCaret.parent();
+      endOffset = endContainer.contents().index(endCaret);
+      if (startContainer[0] === endContainer[0]) {
+        endOffset -= 1;
+      }
+      range = document.createRange();
+      range.setStart(startContainer.get(0), startOffset);
+      range.setEnd(endContainer.get(0), endOffset);
+      startCaret.remove();
+      endCaret.remove();
+      this.range(range);
+    } else {
+      startCaret.remove();
+      endCaret.remove();
+    }
+    this._selectionSaved = false;
+    return range;
+  };
+
+  return Selection;
+
+})(SimpleModule);
+
+Formatter = (function(superClass) {
+  extend(Formatter, superClass);
+
+  function Formatter() {
+    return Formatter.__super__.constructor.apply(this, arguments);
+  }
+
+  Formatter.pluginName = 'Formatter';
+
+  Formatter.prototype.opts = {
+    allowedTags: [],
+    allowedAttributes: {},
+    allowedStyles: {}
+  };
+
+  Formatter.prototype._init = function() {
+    this.editor = this._module;
+    this._allowedTags = $.merge(['br', 'span', 'a', 'img', 'b', 'strong', 'i', 'strike', 'u', 'font', 'p', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'hr'], this.opts.allowedTags);
+    this._allowedAttributes = $.extend({
+      img: ['src', 'alt', 'width', 'height', 'data-non-image'],
+      a: ['href', 'target'],
+      font: ['color'],
+      code: ['class']
+    }, this.opts.allowedAttributes);
+    this._allowedStyles = $.extend({
+      span: ['color', 'font-size'],
+      b: ['color'],
+      i: ['color'],
+      strong: ['color'],
+      strike: ['color'],
+      u: ['color'],
+      p: ['margin-left', 'text-align'],
+      h1: ['margin-left', 'text-align'],
+      h2: ['margin-left', 'text-align'],
+      h3: ['margin-left', 'text-align'],
+      h4: ['margin-left', 'text-align']
+    }, this.opts.allowedStyles);
+    return this.editor.body.on('click', 'a', function(e) {
+      return false;
+    });
+  };
+
+  Formatter.prototype.decorate = function($el) {
+    if ($el == null) {
+      $el = this.editor.body;
+    }
+    this.editor.trigger('decorate', [$el]);
+    return $el;
+  };
+
+  Formatter.prototype.undecorate = function($el) {
+    if ($el == null) {
+      $el = this.editor.body.clone();
+    }
+    this.editor.trigger('undecorate', [$el]);
+    return $el;
+  };
+
+  Formatter.prototype.autolink = function($el) {
+    var $link, $node, findLinkNode, k, lastIndex, len, linkNodes, match, re, replaceEls, subStr, text, uri;
+    if ($el == null) {
+      $el = this.editor.body;
+    }
+    linkNodes = [];
+    findLinkNode = function($parentNode) {
+      return $parentNode.contents().each(function(i, node) {
+        var $node, text;
+        $node = $(node);
+        if ($node.is('a') || $node.closest('a, pre', $el).length) {
+          return;
+        }
+        if (!$node.is('iframe') && $node.contents().length) {
+          return findLinkNode($node);
+        } else if ((text = $node.text()) && /https?:\/\/|www\./ig.test(text)) {
+          return linkNodes.push($node);
+        }
+      });
+    };
+    findLinkNode($el);
+    re = /(https?:\/\/|www\.)[\w\-\.\?&=\/#%:,@\!\+]+/ig;
+    for (k = 0, len = linkNodes.length; k < len; k++) {
+      $node = linkNodes[k];
+      text = $node.text();
+      replaceEls = [];
+      match = null;
+      lastIndex = 0;
+      while ((match = re.exec(text)) !== null) {
+        subStr = text.substring(lastIndex, match.index);
+        replaceEls.push(document.createTextNode(subStr));
+        lastIndex = re.lastIndex;
+        uri = /^(http(s)?:\/\/|\/)/.test(match[0]) ? match[0] : 'http://' + match[0];
+        $link = $("<a href=\"" + uri + "\" rel=\"nofollow\"></a>").text(match[0]);
+        replaceEls.push($link[0]);
+      }
+      replaceEls.push(document.createTextNode(text.substring(lastIndex)));
+      $node.replaceWith($(replaceEls));
+    }
+    return $el;
+  };
+
+  Formatter.prototype.format = function($el) {
+    var $node, blockNode, k, l, len, len1, n, node, ref, ref1;
+    if ($el == null) {
+      $el = this.editor.body;
+    }
+    if ($el.is(':empty')) {
+      $el.append('<p>' + this.editor.util.phBr + '</p>');
+      return $el;
+    }
+    ref = $el.contents();
+    for (k = 0, len = ref.length; k < len; k++) {
+      n = ref[k];
+      this.cleanNode(n, true);
+    }
+    ref1 = $el.contents();
+    for (l = 0, len1 = ref1.length; l < len1; l++) {
+      node = ref1[l];
+      $node = $(node);
+      if ($node.is('br')) {
+        if (typeof blockNode !== "undefined" && blockNode !== null) {
+          blockNode = null;
+        }
+        $node.remove();
+      } else if (this.editor.util.isBlockNode(node)) {
+        if ($node.is('li')) {
+          if (blockNode && blockNode.is('ul, ol')) {
+            blockNode.append(node);
+          } else {
+            blockNode = $('<ul/>').insertBefore(node);
+            blockNode.append(node);
+          }
+        } else {
+          blockNode = null;
+        }
+      } else {
+        if (!blockNode || blockNode.is('ul, ol')) {
+          blockNode = $('<p/>').insertBefore(node);
+        }
+        blockNode.append(node);
+        if (this.editor.util.isEmptyNode(blockNode)) {
+          blockNode.append(this.editor.util.phBr);
+        }
+      }
+    }
+    return $el;
+  };
+
+  Formatter.prototype.cleanNode = function(node, recursive) {
+    var $blockEls, $childImg, $node, $p, $td, allowedAttributes, attr, contents, isDecoration, k, l, len, len1, n, ref, ref1, text, textNode;
+    $node = $(node);
+    if (!($node.length > 0)) {
+      return;
+    }
+    if ($node[0].nodeType === 3) {
+      text = $node.text().replace(/(\r\n|\n|\r)/gm, '');
+      if (text) {
+        textNode = document.createTextNode(text);
+        $node.replaceWith(textNode);
+      } else {
+        $node.remove();
+      }
+      return;
+    }
+    contents = $node.is('iframe') ? null : $node.contents();
+    isDecoration = this.editor.util.isDecoratedNode($node);
+    if ($node.is(this._allowedTags.join(',')) || isDecoration) {
+      if ($node.is('a') && ($childImg = $node.find('img')).length > 0) {
+        $node.replaceWith($childImg);
+        $node = $childImg;
+        contents = null;
+      }
+      if ($node.is('td') && ($blockEls = $node.find(this.editor.util.blockNodes.join(','))).length > 0) {
+        $blockEls.each((function(_this) {
+          return function(i, blockEl) {
+            return $(blockEl).contents().unwrap();
+          };
+        })(this));
+        contents = $node.contents();
+      }
+      if ($node.is('img') && $node.hasClass('uploading')) {
+        $node.remove();
+      }
+      if (!isDecoration) {
+        allowedAttributes = this._allowedAttributes[$node[0].tagName.toLowerCase()];
+        ref = $.makeArray($node[0].attributes);
+        for (k = 0, len = ref.length; k < len; k++) {
+          attr = ref[k];
+          if (attr.name === 'style') {
+            continue;
+          }
+          if (!((allowedAttributes != null) && (ref1 = attr.name, indexOf.call(allowedAttributes, ref1) >= 0))) {
+            $node.removeAttr(attr.name);
+          }
+        }
+        this._cleanNodeStyles($node);
+        if ($node.is('span') && $node[0].attributes.length === 0) {
+          $node.contents().first().unwrap();
+        }
+      }
+    } else if ($node[0].nodeType === 1 && !$node.is(':empty')) {
+      if ($node.is('div, article, dl, header, footer, tr')) {
+        $node.append('<br/>');
+        contents.first().unwrap();
+      } else if ($node.is('table')) {
+        $p = $('<p/>');
+        $node.find('tr').each(function(i, tr) {
+          return $p.append($(tr).text() + '<br/>');
+        });
+        $node.replaceWith($p);
+        contents = null;
+      } else if ($node.is('thead, tfoot')) {
+        $node.remove();
+        contents = null;
+      } else if ($node.is('th')) {
+        $td = $('<td/>').append($node.contents());
+        $node.replaceWith($td);
+      } else {
+        contents.first().unwrap();
+      }
+    } else {
+      $node.remove();
+      contents = null;
+    }
+    if (recursive && (contents != null) && !$node.is('pre')) {
+      for (l = 0, len1 = contents.length; l < len1; l++) {
+        n = contents[l];
+        this.cleanNode(n, true);
+      }
+    }
+    return null;
+  };
+
+  Formatter.prototype._cleanNodeStyles = function($node) {
+    var allowedStyles, k, len, pair, ref, ref1, style, styleStr, styles;
+    styleStr = $node.attr('style');
+    if (!styleStr) {
+      return;
+    }
+    $node.removeAttr('style');
+    allowedStyles = this._allowedStyles[$node[0].tagName.toLowerCase()];
+    if (!(allowedStyles && allowedStyles.length > 0)) {
+      return $node;
+    }
+    styles = {};
+    ref = styleStr.split(';');
+    for (k = 0, len = ref.length; k < len; k++) {
+      style = ref[k];
+      style = $.trim(style);
+      pair = style.split(':');
+      if (pair.length !== 2) {
+        continue;
+      }
+      if (pair[0] === 'font-size' && pair[1].indexOf('px') > 0) {
+        if (parseInt(pair[1], 10) < 12) {
+          continue;
+        }
+      }
+      if (ref1 = pair[0], indexOf.call(allowedStyles, ref1) >= 0) {
+        styles[$.trim(pair[0])] = $.trim(pair[1]);
+      }
+    }
+    if (Object.keys(styles).length > 0) {
+      $node.css(styles);
+    }
+    return $node;
+  };
+
+  Formatter.prototype.clearHtml = function(html, lineBreak) {
+    var container, contents, result;
+    if (lineBreak == null) {
+      lineBreak = true;
+    }
+    container = $('<div/>').append(html);
+    contents = container.contents();
+    result = '';
+    contents.each((function(_this) {
+      return function(i, node) {
+        var $node, children;
+        if (node.nodeType === 3) {
+          return result += node.nodeValue;
+        } else if (node.nodeType === 1) {
+          $node = $(node);
+          children = $node.is('iframe') ? null : $node.contents();
+          if (children && children.length > 0) {
+            result += _this.clearHtml(children);
+          }
+          if (lineBreak && i < contents.length - 1 && $node.is('br, p, div, li,tr, pre, address, artticle, aside, dl, figcaption, footer, h1, h2,h3, h4, header')) {
+            return result += '\n';
+          }
+        }
+      };
+    })(this));
+    return result;
+  };
+
+  Formatter.prototype.beautify = function($contents) {
+    var uselessP;
+    uselessP = function($el) {
+      return !!($el.is('p') && !$el.text() && $el.children(':not(br)').length < 1);
+    };
+    return $contents.each(function(i, el) {
+      var $el, invalid;
+      $el = $(el);
+      invalid = $el.is(':not(img, br, col, td, hr, [class^="simditor-"]):empty');
+      if (invalid || uselessP($el)) {
+        $el.remove();
+      }
+      return $el.find(':not(img, br, col, td, hr, [class^="simditor-"]):empty').remove();
+    });
+  };
+
+  return Formatter;
+
+})(SimpleModule);
+
+InputManager = (function(superClass) {
+  extend(InputManager, superClass);
+
+  function InputManager() {
+    return InputManager.__super__.constructor.apply(this, arguments);
+  }
+
+  InputManager.pluginName = 'InputManager';
+
+  InputManager.prototype._modifierKeys = [16, 17, 18, 91, 93, 224];
+
+  InputManager.prototype._arrowKeys = [37, 38, 39, 40];
+
+  InputManager.prototype._init = function() {
+    var selectAllKey, submitKey;
+    this.editor = this._module;
+    this.throttledValueChanged = this.editor.util.throttle((function(_this) {
+      return function(params) {
+        return setTimeout(function() {
+          return _this.editor.trigger('valuechanged', params);
+        }, 10);
+      };
+    })(this), 300);
+    this.throttledSelectionChanged = this.editor.util.throttle((function(_this) {
+      return function() {
+        return _this.editor.trigger('selectionchanged');
+      };
+    })(this), 50);
+    $(document).on('selectionchange.simditor' + this.editor.id, (function(_this) {
+      return function(e) {
+        var triggerEvent;
+        if (!(_this.focused && !_this.editor.clipboard.pasting)) {
+          return;
+        }
+        triggerEvent = function() {
+          if (_this._selectionTimer) {
+            clearTimeout(_this._selectionTimer);
+            _this._selectionTimer = null;
+          }
+          if (_this.editor.selection._selection.rangeCount > 0) {
+            return _this.throttledSelectionChanged();
+          } else {
+            return _this._selectionTimer = setTimeout(function() {
+              _this._selectionTimer = null;
+              if (_this.focused) {
+                return triggerEvent();
+              }
+            }, 10);
+          }
+        };
+        return triggerEvent();
+      };
+    })(this));
+    this.editor.on('valuechanged', (function(_this) {
+      return function() {
+        var $rootBlocks;
+        _this.lastCaretPosition = null;
+        $rootBlocks = _this.editor.body.children().filter(function(i, node) {
+          return _this.editor.util.isBlockNode(node);
+        });
+        if (_this.focused && $rootBlocks.length === 0) {
+          _this.editor.selection.save();
+          _this.editor.formatter.format();
+          _this.editor.selection.restore();
+        }
+        _this.editor.body.find('hr, pre, .simditor-table').each(function(i, el) {
+          var $el, formatted;
+          $el = $(el);
+          if ($el.parent().is('blockquote') || $el.parent()[0] === _this.editor.body[0]) {
+            formatted = false;
+            if ($el.next().length === 0) {
+              $('<p/>').append(_this.editor.util.phBr).insertAfter($el);
+              formatted = true;
+            }
+            if ($el.prev().length === 0) {
+              $('<p/>').append(_this.editor.util.phBr).insertBefore($el);
+              formatted = true;
+            }
+            if (formatted) {
+              return _this.throttledValueChanged();
+            }
+          }
+        });
+        _this.editor.body.find('pre:empty').append(_this.editor.util.phBr);
+        if (!_this.editor.util.support.onselectionchange && _this.focused) {
+          return _this.throttledSelectionChanged();
+        }
+      };
+    })(this));
+    this.editor.body.on('keydown', $.proxy(this._onKeyDown, this)).on('keypress', $.proxy(this._onKeyPress, this)).on('keyup', $.proxy(this._onKeyUp, this)).on('mouseup', $.proxy(this._onMouseUp, this)).on('focus', $.proxy(this._onFocus, this)).on('blur', $.proxy(this._onBlur, this)).on('drop', $.proxy(this._onDrop, this)).on('input', $.proxy(this._onInput, this));
+    if (this.editor.util.browser.firefox) {
+      this.editor.hotkeys.add('cmd+left', (function(_this) {
+        return function(e) {
+          e.preventDefault();
+          _this.editor.selection._selection.modify('move', 'backward', 'lineboundary');
+          return false;
+        };
+      })(this));
+      this.editor.hotkeys.add('cmd+right', (function(_this) {
+        return function(e) {
+          e.preventDefault();
+          _this.editor.selection._selection.modify('move', 'forward', 'lineboundary');
+          return false;
+        };
+      })(this));
+      selectAllKey = this.editor.util.os.mac ? 'cmd+a' : 'ctrl+a';
+      this.editor.hotkeys.add(selectAllKey, (function(_this) {
+        return function(e) {
+          var $children, firstBlock, lastBlock, range;
+          $children = _this.editor.body.children();
+          if (!($children.length > 0)) {
+            return;
+          }
+          firstBlock = $children.first().get(0);
+          lastBlock = $children.last().get(0);
+          range = document.createRange();
+          range.setStart(firstBlock, 0);
+          range.setEnd(lastBlock, _this.editor.util.getNodeLength(lastBlock));
+          _this.editor.selection.range(range);
+          return false;
+        };
+      })(this));
+    }
+    submitKey = this.editor.util.os.mac ? 'cmd+enter' : 'ctrl+enter';
+    return this.editor.hotkeys.add(submitKey, (function(_this) {
+      return function(e) {
+        _this.editor.el.closest('form').find('button:submit').click();
+        return false;
+      };
+    })(this));
+  };
+
+  InputManager.prototype._onFocus = function(e) {
+    if (this.editor.clipboard.pasting) {
+      return;
+    }
+    this.editor.el.addClass('focus').removeClass('error');
+    this.focused = true;
+    return setTimeout((function(_this) {
+      return function() {
+        var $blockEl, range;
+        range = _this.editor.selection._selection.getRangeAt(0);
+        if (range.startContainer === _this.editor.body[0]) {
+          if (_this.lastCaretPosition) {
+            _this.editor.undoManager.caretPosition(_this.lastCaretPosition);
+          } else {
+            $blockEl = _this.editor.body.children().first();
+            range = document.createRange();
+            _this.editor.selection.setRangeAtStartOf($blockEl, range);
+          }
+        }
+        _this.lastCaretPosition = null;
+        _this.editor.triggerHandler('focus');
+        if (!_this.editor.util.support.onselectionchange) {
+          return _this.throttledSelectionChanged();
+        }
+      };
+    })(this), 0);
+  };
+
+  InputManager.prototype._onBlur = function(e) {
+    var ref;
+    if (this.editor.clipboard.pasting) {
+      return;
+    }
+    this.editor.el.removeClass('focus');
+    this.editor.sync();
+    this.focused = false;
+    this.lastCaretPosition = (ref = this.editor.undoManager.currentState()) != null ? ref.caret : void 0;
+    return this.editor.triggerHandler('blur');
+  };
+
+  InputManager.prototype._onMouseUp = function(e) {
+    if (!this.editor.util.support.onselectionchange) {
+      return this.throttledSelectionChanged();
+    }
+  };
+
+  InputManager.prototype._onKeyDown = function(e) {
+    var ref, ref1;
+    if (this.editor.triggerHandler(e) === false) {
+      return false;
+    }
+    if (this.editor.hotkeys.respondTo(e)) {
+      return;
+    }
+    if (this.editor.keystroke.respondTo(e)) {
+      this.throttledValueChanged();
+      return false;
+    }
+    if ((ref = e.which, indexOf.call(this._modifierKeys, ref) >= 0) || (ref1 = e.which, indexOf.call(this._arrowKeys, ref1) >= 0)) {
+      return;
+    }
+    if (this.editor.util.metaKey(e) && e.which === 86) {
+      return;
+    }
+    if (!this.editor.util.support.oninput) {
+      this.throttledValueChanged(['typing']);
+    }
+    return null;
+  };
+
+  InputManager.prototype._onKeyPress = function(e) {
+    if (this.editor.triggerHandler(e) === false) {
+      return false;
+    }
+  };
+
+  InputManager.prototype._onKeyUp = function(e) {
+    var p, ref;
+    if (this.editor.triggerHandler(e) === false) {
+      return false;
+    }
+    if (!this.editor.util.support.onselectionchange && (ref = e.which, indexOf.call(this._arrowKeys, ref) >= 0)) {
+      this.throttledValueChanged();
+      return;
+    }
+    if ((e.which === 8 || e.which === 46) && this.editor.util.isEmptyNode(this.editor.body)) {
+      this.editor.body.empty();
+      p = $('<p/>').append(this.editor.util.phBr).appendTo(this.editor.body);
+      this.editor.selection.setRangeAtStartOf(p);
+    }
+  };
+
+  InputManager.prototype._onDrop = function(e) {
+    if (this.editor.triggerHandler(e) === false) {
+      return false;
+    }
+    return this.throttledValueChanged();
+  };
+
+  InputManager.prototype._onInput = function(e) {
+    return this.throttledValueChanged(['oninput']);
+  };
+
+  return InputManager;
+
+})(SimpleModule);
+
+Keystroke = (function(superClass) {
+  extend(Keystroke, superClass);
+
+  function Keystroke() {
+    return Keystroke.__super__.constructor.apply(this, arguments);
+  }
+
+  Keystroke.pluginName = 'Keystroke';
+
+  Keystroke.prototype._init = function() {
+    this.editor = this._module;
+    this._keystrokeHandlers = {};
+    return this._initKeystrokeHandlers();
+  };
+
+  Keystroke.prototype.add = function(key, node, handler) {
+    key = key.toLowerCase();
+    key = this.editor.hotkeys.constructor.aliases[key] || key;
+    if (!this._keystrokeHandlers[key]) {
+      this._keystrokeHandlers[key] = {};
+    }
+    return this._keystrokeHandlers[key][node] = handler;
+  };
+
+  Keystroke.prototype.respondTo = function(e) {
+    var base, key, ref, result;
+    key = (ref = this.editor.hotkeys.constructor.keyNameMap[e.which]) != null ? ref.toLowerCase() : void 0;
+    if (!key) {
+      return;
+    }
+    if (key in this._keystrokeHandlers) {
+      result = typeof (base = this._keystrokeHandlers[key])['*'] === "function" ? base['*'](e) : void 0;
+      if (!result) {
+        this.editor.selection.startNodes().each((function(_this) {
+          return function(i, node) {
+            var handler, ref1;
+            if (node.nodeType !== Node.ELEMENT_NODE) {
+              return;
+            }
+            handler = (ref1 = _this._keystrokeHandlers[key]) != null ? ref1[node.tagName.toLowerCase()] : void 0;
+            result = typeof handler === "function" ? handler(e, $(node)) : void 0;
+            if (result === true || result === false) {
+              return false;
+            }
+          };
+        })(this));
+      }
+      if (result) {
+        return true;
+      }
+    }
+  };
+
+  Keystroke.prototype._initKeystrokeHandlers = function() {
+    var titleEnterHandler;
+    if (this.editor.util.browser.safari) {
+      this.add('enter', '*', (function(_this) {
+        return function(e) {
+          var $blockEl, $br;
+          if (!e.shiftKey) {
+            return;
+          }
+          $blockEl = _this.editor.selection.blockNodes().last();
+          if ($blockEl.is('pre')) {
+            return;
+          }
+          $br = $('<br/>');
+          if (_this.editor.selection.rangeAtEndOf($blockEl)) {
+            _this.editor.selection.insertNode($br);
+            _this.editor.selection.insertNode($('<br/>'));
+            _this.editor.selection.setRangeBefore($br);
+          } else {
+            _this.editor.selection.insertNode($br);
+          }
+          return true;
+        };
+      })(this));
+    }
+    if (this.editor.util.browser.webkit || this.editor.util.browser.msie) {
+      titleEnterHandler = (function(_this) {
+        return function(e, $node) {
+          var $p;
+          if (!_this.editor.selection.rangeAtEndOf($node)) {
+            return;
+          }
+          $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node);
+          _this.editor.selection.setRangeAtStartOf($p);
+          return true;
+        };
+      })(this);
+      this.add('enter', 'h1', titleEnterHandler);
+      this.add('enter', 'h2', titleEnterHandler);
+      this.add('enter', 'h3', titleEnterHandler);
+      this.add('enter', 'h4', titleEnterHandler);
+      this.add('enter', 'h5', titleEnterHandler);
+      this.add('enter', 'h6', titleEnterHandler);
+    }
+    this.add('backspace', '*', (function(_this) {
+      return function(e) {
+        var $blockEl, $prevBlockEl, $rootBlock, isWebkit;
+        $rootBlock = _this.editor.selection.rootNodes().first();
+        $prevBlockEl = $rootBlock.prev();
+        if ($prevBlockEl.is('hr') && _this.editor.selection.rangeAtStartOf($rootBlock)) {
+          _this.editor.selection.save();
+          $prevBlockEl.remove();
+          _this.editor.selection.restore();
+          return true;
+        }
+        $blockEl = _this.editor.selection.blockNodes().last();
+        isWebkit = _this.editor.util.browser.webkit;
+        if (isWebkit && _this.editor.selection.rangeAtStartOf($blockEl)) {
+          _this.editor.selection.save();
+          _this.editor.formatter.cleanNode($blockEl, true);
+          _this.editor.selection.restore();
+          return null;
+        }
+      };
+    })(this));
+    this.add('enter', 'li', (function(_this) {
+      return function(e, $node) {
+        var $cloneNode, listEl, newBlockEl, newListEl;
+        $cloneNode = $node.clone();
+        $cloneNode.find('ul, ol').remove();
+        if (!(_this.editor.util.isEmptyNode($cloneNode) && $node.is(_this.editor.selection.blockNodes().last()))) {
+          return;
+        }
+        listEl = $node.parent();
+        if ($node.next('li').length > 0) {
+          if (!_this.editor.util.isEmptyNode($node)) {
+            return;
+          }
+          if (listEl.parent('li').length > 0) {
+            newBlockEl = $('<li/>').append(_this.editor.util.phBr).insertAfter(listEl.parent('li'));
+            newListEl = $('<' + listEl[0].tagName + '/>').append($node.nextAll('li'));
+            newBlockEl.append(newListEl);
+          } else {
+            newBlockEl = $('<p/>').append(_this.editor.util.phBr).insertAfter(listEl);
+            newListEl = $('<' + listEl[0].tagName + '/>').append($node.nextAll('li'));
+            newBlockEl.after(newListEl);
+          }
+        } else {
+          if (listEl.parent('li').length > 0) {
+            newBlockEl = $('<li/>').insertAfter(listEl.parent('li'));
+            if ($node.contents().length > 0) {
+              newBlockEl.append($node.contents());
+            } else {
+              newBlockEl.append(_this.editor.util.phBr);
+            }
+          } else {
+            newBlockEl = $('<p/>').append(_this.editor.util.phBr).insertAfter(listEl);
+            if ($node.children('ul, ol').length > 0) {
+              newBlockEl.after($node.children('ul, ol'));
+            }
+          }
+        }
+        if ($node.prev('li').length) {
+          $node.remove();
+        } else {
+          listEl.remove();
+        }
+        _this.editor.selection.setRangeAtStartOf(newBlockEl);
+        return true;
+      };
+    })(this));
+    this.add('enter', 'pre', (function(_this) {
+      return function(e, $node) {
+        var $p, breakNode, range;
+        e.preventDefault();
+        if (e.shiftKey) {
+          $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node);
+          _this.editor.selection.setRangeAtStartOf($p);
+          return true;
+        }
+        range = _this.editor.selection.range();
+        breakNode = null;
+        range.deleteContents();
+        if (!_this.editor.util.browser.msie && _this.editor.selection.rangeAtEndOf($node)) {
+          breakNode = document.createTextNode('\n\n');
+          range.insertNode(breakNode);
+          range.setEnd(breakNode, 1);
+        } else {
+          breakNode = document.createTextNode('\n');
+          range.insertNode(breakNode);
+          range.setStartAfter(breakNode);
+        }
+        range.collapse(false);
+        _this.editor.selection.range(range);
+        return true;
+      };
+    })(this));
+    this.add('enter', 'blockquote', (function(_this) {
+      return function(e, $node) {
+        var $closestBlock, range;
+        $closestBlock = _this.editor.selection.blockNodes().last();
+        if (!($closestBlock.is('p') && !$closestBlock.next().length && _this.editor.util.isEmptyNode($closestBlock))) {
+          return;
+        }
+        $node.after($closestBlock);
+        range = document.createRange();
+        _this.editor.selection.setRangeAtStartOf($closestBlock, range);
+        return true;
+      };
+    })(this));
+    this.add('backspace', 'li', (function(_this) {
+      return function(e, $node) {
+        var $br, $childList, $newLi, $prevChildList, $prevNode, $textNode, isFF, range, text;
+        $childList = $node.children('ul, ol');
+        $prevNode = $node.prev('li');
+        if (!($childList.length > 0 && $prevNode.length > 0)) {
+          return false;
+        }
+        text = '';
+        $textNode = null;
+        $node.contents().each(function(i, n) {
+          if (n.nodeType === 1 && /UL|OL/.test(n.nodeName)) {
+            return false;
+          }
+          if (n.nodeType === 1 && /BR/.test(n.nodeName)) {
+            return;
+          }
+          if (n.nodeType === 3 && n.nodeValue) {
+            text += n.nodeValue;
+          } else if (n.nodeType === 1) {
+            text += $(n).text();
+          }
+          return $textNode = $(n);
+        });
+        isFF = _this.editor.util.browser.firefox && !$textNode.next('br').length;
+        if ($textNode && text.length === 1 && isFF) {
+          $br = $(_this.editor.util.phBr).insertAfter($textNode);
+          $textNode.remove();
+          _this.editor.selection.setRangeBefore($br);
+          return true;
+        } else if (text.length > 0) {
+          return false;
+        }
+        range = document.createRange();
+        $prevChildList = $prevNode.children('ul, ol');
+        if ($prevChildList.length > 0) {
+          $newLi = $('<li/>').append(_this.editor.util.phBr).appendTo($prevChildList);
+          $prevChildList.append($childList.children('li'));
+          $node.remove();
+          _this.editor.selection.setRangeAtEndOf($newLi, range);
+        } else {
+          _this.editor.selection.setRangeAtEndOf($prevNode, range);
+          $prevNode.append($childList);
+          $node.remove();
+          _this.editor.selection.range(range);
+        }
+        return true;
+      };
+    })(this));
+    this.add('backspace', 'pre', (function(_this) {
+      return function(e, $node) {
+        var $newNode, codeStr, range;
+        if (!_this.editor.selection.rangeAtStartOf($node)) {
+          return;
+        }
+        codeStr = $node.html().replace('\n', '<br/>') || _this.editor.util.phBr;
+        $newNode = $('<p/>').append(codeStr).insertAfter($node);
+        $node.remove();
+        range = document.createRange();
+        _this.editor.selection.setRangeAtStartOf($newNode, range);
+        return true;
+      };
+    })(this));
+    return this.add('backspace', 'blockquote', (function(_this) {
+      return function(e, $node) {
+        var $firstChild, range;
+        if (!_this.editor.selection.rangeAtStartOf($node)) {
+          return;
+        }
+        $firstChild = $node.children().first().unwrap();
+        range = document.createRange();
+        _this.editor.selection.setRangeAtStartOf($firstChild, range);
+        return true;
+      };
+    })(this));
+  };
+
+  return Keystroke;
+
+})(SimpleModule);
+
+UndoManager = (function(superClass) {
+  extend(UndoManager, superClass);
+
+  function UndoManager() {
+    return UndoManager.__super__.constructor.apply(this, arguments);
+  }
+
+  UndoManager.pluginName = 'UndoManager';
+
+  UndoManager.prototype._index = -1;
+
+  UndoManager.prototype._capacity = 20;
+
+  UndoManager.prototype._startPosition = null;
+
+  UndoManager.prototype._endPosition = null;
+
+  UndoManager.prototype._init = function() {
+    var redoShortcut, undoShortcut;
+    this.editor = this._module;
+    this._stack = [];
+    if (this.editor.util.os.mac) {
+      undoShortcut = 'cmd+z';
+      redoShortcut = 'shift+cmd+z';
+    } else if (this.editor.util.os.win) {
+      undoShortcut = 'ctrl+z';
+      redoShortcut = 'ctrl+y';
+    } else {
+      undoShortcut = 'ctrl+z';
+      redoShortcut = 'shift+ctrl+z';
+    }
+    this.editor.hotkeys.add(undoShortcut, (function(_this) {
+      return function(e) {
+        e.preventDefault();
+        _this.undo();
+        return false;
+      };
+    })(this));
+    this.editor.hotkeys.add(redoShortcut, (function(_this) {
+      return function(e) {
+        e.preventDefault();
+        _this.redo();
+        return false;
+      };
+    })(this));
+    this.throttledPushState = this.editor.util.throttle((function(_this) {
+      return function() {
+        return _this._pushUndoState();
+      };
+    })(this), 2000);
+    this.editor.on('valuechanged', (function(_this) {
+      return function(e, src) {
+        if (src === 'undo' || src === 'redo') {
+          return;
+        }
+        return _this.throttledPushState();
+      };
+    })(this));
+    this.editor.on('selectionchanged', (function(_this) {
+      return function(e) {
+        _this.resetCaretPosition();
+        return _this.update();
+      };
+    })(this));
+    this.editor.on('focus', (function(_this) {
+      return function(e) {
+        if (_this._stack.length === 0) {
+          return _this._pushUndoState();
+        }
+      };
+    })(this));
+    return this.editor.on('blur', (function(_this) {
+      return function(e) {
+        return _this.resetCaretPosition();
+      };
+    })(this));
+  };
+
+  UndoManager.prototype.resetCaretPosition = function() {
+    this._startPosition = null;
+    return this._endPosition = null;
+  };
+
+  UndoManager.prototype.startPosition = function() {
+    if (this.editor.selection._range) {
+      this._startPosition || (this._startPosition = this._getPosition('start'));
+    }
+    return this._startPosition;
+  };
+
+  UndoManager.prototype.endPosition = function() {
+    if (this.editor.selection._range) {
+      this._endPosition || (this._endPosition = (function(_this) {
+        return function() {
+          var range;
+          range = _this.editor.selection.range();
+          if (range.collapsed) {
+            return _this._startPosition;
+          }
+          return _this._getPosition('end');
+        };
+      })(this)());
+    }
+    return this._endPosition;
+  };
+
+  UndoManager.prototype._pushUndoState = function() {
+    var caret;
+    if (this.editor.triggerHandler('pushundostate') === false) {
+      return;
+    }
+    caret = this.caretPosition();
+    if (!caret.start) {
+      return;
+    }
+    this._index += 1;
+    this._stack.length = this._index;
+    this._stack.push({
+      html: this.editor.body.html(),
+      caret: this.caretPosition()
+    });
+    if (this._stack.length > this._capacity) {
+      this._stack.shift();
+      return this._index -= 1;
+    }
+  };
+
+  UndoManager.prototype.currentState = function() {
+    if (this._stack.length && this._index > -1) {
+      return this._stack[this._index];
+    } else {
+      return null;
+    }
+  };
+
+  UndoManager.prototype.undo = function() {
+    var state;
+    if (this._index < 1 || this._stack.length < 2) {
+      return;
+    }
+    this.editor.hidePopover();
+    this._index -= 1;
+    state = this._stack[this._index];
+    this.editor.body.get(0).innerHTML = state.html;
+    this.caretPosition(state.caret);
+    this.editor.body.find('.selected').removeClass('selected');
+    this.editor.sync();
+    return this.editor.trigger('valuechanged', ['undo']);
+  };
+
+  UndoManager.prototype.redo = function() {
+    var state;
+    if (this._index < 0 || this._stack.length < this._index + 2) {
+      return;
+    }
+    this.editor.hidePopover();
+    this._index += 1;
+    state = this._stack[this._index];
+    this.editor.body.get(0).innerHTML = state.html;
+    this.caretPosition(state.caret);
+    this.editor.body.find('.selected').removeClass('selected');
+    this.editor.sync();
+    return this.editor.trigger('valuechanged', ['redo']);
+  };
+
+  UndoManager.prototype.update = function() {
+    var currentState;
+    currentState = this.currentState();
+    if (!currentState) {
+      return;
+    }
+    currentState.html = this.editor.body.html();
+    return currentState.caret = this.caretPosition();
+  };
+
+  UndoManager.prototype._getNodeOffset = function(node, index) {
+    var $parent, merging, offset;
+    if ($.isNumeric(index)) {
+      $parent = $(node);
+    } else {
+      $parent = $(node).parent();
+    }
+    offset = 0;
+    merging = false;
+    $parent.contents().each(function(i, child) {
+      if (node === child || (index === i && i === 0)) {
+        return false;
+      }
+      if (child.nodeType === Node.TEXT_NODE) {
+        if (!merging && child.nodeValue.length > 0) {
+          offset += 1;
+          merging = true;
+        }
+      } else {
+        offset += 1;
+        merging = false;
+      }
+      if (index - 1 === i) {
+        return false;
+      }
+      return null;
+    });
+    return offset;
+  };
+
+  UndoManager.prototype._getPosition = function(type) {
+    var $nodes, node, nodes, offset, position, prevNode, range;
+    if (type == null) {
+      type = 'start';
+    }
+    range = this.editor.selection.range();
+    offset = range[type + "Offset"];
+    $nodes = this.editor.selection[type + "Nodes"]();
+    node = $nodes.first()[0];
+    if (node.nodeType === Node.TEXT_NODE) {
+      prevNode = node.previousSibling;
+      while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {
+        node = prevNode;
+        offset += this.editor.util.getNodeLength(prevNode);
+        prevNode = prevNode.previousSibling;
+      }
+      nodes = $nodes.get();
+      nodes[0] = node;
+      $nodes = $(nodes);
+    } else {
+      offset = this._getNodeOffset(node, offset);
+    }
+    position = [offset];
+    $nodes.each((function(_this) {
+      return function(i, node) {
+        return position.unshift(_this._getNodeOffset(node));
+      };
+    })(this));
+    return position;
+  };
+
+  UndoManager.prototype._getNodeByPosition = function(position) {
+    var child, childNodes, i, k, len, node, offset, ref;
+    node = this.editor.body[0];
+    ref = position.slice(0, position.length - 1);
+    for (i = k = 0, len = ref.length; k < len; i = ++k) {
+      offset = ref[i];
+      childNodes = node.childNodes;
+      if (offset > childNodes.length - 1) {
+        if (i === position.length - 2 && $(node).is('pre:empty')) {
+          child = document.createTextNode('');
+          node.appendChild(child);
+          childNodes = node.childNodes;
+        } else {
+          node = null;
+          break;
+        }
+      }
+      node = childNodes[offset];
+    }
+    return node;
+  };
+
+  UndoManager.prototype.caretPosition = function(caret) {
+    var endContainer, endOffset, range, startContainer, startOffset;
+    if (!caret) {
+      range = this.editor.selection.range();
+      caret = this.editor.inputManager.focused && (range != null) ? {
+        start: this.startPosition(),
+        end: this.endPosition(),
+        collapsed: range.collapsed
+      } : {};
+      return caret;
+    } else {
+      if (!caret.start) {
+        return;
+      }
+      startContainer = this._getNodeByPosition(caret.start);
+      startOffset = caret.start[caret.start.length - 1];
+      if (caret.collapsed) {
+        endContainer = startContainer;
+        endOffset = startOffset;
+      } else {
+        endContainer = this._getNodeByPosition(caret.end);
+        endOffset = caret.start[caret.start.length - 1];
+      }
+      if (!startContainer || !endContainer) {
+        if (typeof console !== "undefined" && console !== null) {
+          if (typeof console.warn === "function") {
+            console.warn('simditor: invalid caret state');
+          }
+        }
+        return;
+      }
+
+      range = document.createRange();
+      range.setStart(startContainer, startOffset);
+      range.setEnd(endContainer, endOffset);
+      return this.editor.selection.range(range);
+    }
+  };
+
+  return UndoManager;
+
+})(SimpleModule);
+
+Util = (function(superClass) {
+  extend(Util, superClass);
+
+  function Util() {
+    return Util.__super__.constructor.apply(this, arguments);
+  }
+
+  Util.pluginName = 'Util';
+
+  Util.prototype._init = function() {
+    this.editor = this._module;
+    if (this.browser.msie && this.browser.version < 11) {
+      return this.phBr = '';
+    }
+  };
+
+  Util.prototype.phBr = '<br/>';
+
+  Util.prototype.os = (function() {
+    var os;
+    os = {};
+    if (/Mac/.test(navigator.appVersion)) {
+      os.mac = true;
+    } else if (/Linux/.test(navigator.appVersion)) {
+      os.linux = true;
+    } else if (/Win/.test(navigator.appVersion)) {
+      os.win = true;
+    } else if (/X11/.test(navigator.appVersion)) {
+      os.unix = true;
+    }
+    if (/Mobi/.test(navigator.appVersion)) {
+      os.mobile = true;
+    }
+    return os;
+  })();
+
+  Util.prototype.browser = (function() {
+    var chrome, edge, firefox, ie, ref, ref1, ref2, ref3, ref4, safari, ua;
+    ua = navigator.userAgent;
+    ie = /(msie|trident)/i.test(ua);
+    chrome = /chrome|crios/i.test(ua);
+    safari = /safari/i.test(ua) && !chrome;
+    firefox = /firefox/i.test(ua);
+    edge = /edge/i.test(ua);
+    if (ie) {
+      return {
+        msie: true,
+        version: ((ref = ua.match(/(msie |rv:)(\d+(\.\d+)?)/i)) != null ? ref[2] : void 0) * 1
+      };
+    } else if (edge) {
+      return {
+        edge: true,
+        webkit: true,
+        version: ((ref1 = ua.match(/edge\/(\d+(\.\d+)?)/i)) != null ? ref1[1] : void 0) * 1
+      };
+    } else if (chrome) {
+      return {
+        webkit: true,
+        chrome: true,
+        version: ((ref2 = ua.match(/(?:chrome|crios)\/(\d+(\.\d+)?)/i)) != null ? ref2[1] : void 0) * 1
+      };
+    } else if (safari) {
+      return {
+        webkit: true,
+        safari: true,
+        version: ((ref3 = ua.match(/version\/(\d+(\.\d+)?)/i)) != null ? ref3[1] : void 0) * 1
+      };
+    } else if (firefox) {
+      return {
+        mozilla: true,
+        firefox: true,
+        version: ((ref4 = ua.match(/firefox\/(\d+(\.\d+)?)/i)) != null ? ref4[1] : void 0) * 1
+      };
+    } else {
+      return {};
+    }
+  })();
+
+  Util.prototype.support = (function() {
+    return {
+      onselectionchange: (function() {
+        var e, onselectionchange;
+        onselectionchange = document.onselectionchange;
+        if (onselectionchange !== void 0) {
+          try {
+            document.onselectionchange = 0;
+            return document.onselectionchange === null;
+          } catch (_error) {
+            e = _error;
+          } finally {
+            document.onselectionchange = onselectionchange;
+          }
+        }
+        return false;
+      })(),
+      oninput: (function() {
+        return !/(msie|trident)/i.test(navigator.userAgent);
+      })()
+    };
+  })();
+
+  Util.prototype.reflow = function(el) {
+    if (el == null) {
+      el = document;
+    }
+    return $(el)[0].offsetHeight;
+  };
+
+  Util.prototype.metaKey = function(e) {
+    var isMac;
+    isMac = /Mac/.test(navigator.userAgent);
+    if (isMac) {
+      return e.metaKey;
+    } else {
+      return e.ctrlKey;
+    }
+  };
+
+  Util.prototype.isEmptyNode = function(node) {
+    var $node;
+    $node = $(node);
+    return $node.is(':empty') || (!$node.text() && !$node.find(':not(br, span, div)').length);
+  };
+
+  Util.prototype.isDecoratedNode = function(node) {
+    return $(node).is('[class^="simditor-"]');
+  };
+
+  Util.prototype.blockNodes = ["div", "p", "ul", "ol", "li", "blockquote", "hr", "pre", "h1", "h2", "h3", "h4", "h5", "table"];
+
+  Util.prototype.isBlockNode = function(node) {
+    node = $(node)[0];
+    if (!node || node.nodeType === 3) {
+      return false;
+    }
+    return new RegExp("^(" + (this.blockNodes.join('|')) + ")$").test(node.nodeName.toLowerCase());
+  };
+
+  Util.prototype.getNodeLength = function(node) {
+    node = $(node)[0];
+    switch (node.nodeType) {
+      case 7:
+      case 10:
+        return 0;
+      case 3:
+      case 8:
+        return node.length;
+      default:
+        return node.childNodes.length;
+    }
+  };
+
+  Util.prototype.dataURLtoBlob = function(dataURL) {
+    var BlobBuilder, arrayBuffer, bb, blobArray, byteString, hasArrayBufferViewSupport, hasBlobConstructor, i, intArray, k, mimeString, ref, supportBlob;
+    hasBlobConstructor = window.Blob && (function() {
+      var e;
+      try {
+        return Boolean(new Blob());
+      } catch (_error) {
+        e = _error;
+        return false;
+      }
+    })();
+    hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && (function() {
+      var e;
+      try {
+        return new Blob([new Uint8Array(100)]).size === 100;
+      } catch (_error) {
+        e = _error;
+        return false;
+      }
+    })();
+    BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
+    supportBlob = hasBlobConstructor || BlobBuilder;
+    if (!(supportBlob && window.atob && window.ArrayBuffer && window.Uint8Array)) {
+      return false;
+    }
+    if (dataURL.split(',')[0].indexOf('base64') >= 0) {
+      byteString = atob(dataURL.split(',')[1]);
+    } else {
+      byteString = decodeURIComponent(dataURL.split(',')[1]);
+    }
+    arrayBuffer = new ArrayBuffer(byteString.length);
+    intArray = new Uint8Array(arrayBuffer);
+    for (i = k = 0, ref = byteString.length; 0 <= ref ? k <= ref : k >= ref; i = 0 <= ref ? ++k : --k) {
+      intArray[i] = byteString.charCodeAt(i);
+    }
+    mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
+    if (hasBlobConstructor) {
+      blobArray = hasArrayBufferViewSupport ? intArray : arrayBuffer;
+      return new Blob([blobArray], {
+        type: mimeString
+      });
+    }
+    bb = new BlobBuilder();
+    bb.append(arrayBuffer);
+    return bb.getBlob(mimeString);
+  };
+
+  Util.prototype.throttle = function(func, wait) {
+    var args, call, ctx, last, rtn, throttled, timeoutID;
+    last = 0;
+    timeoutID = 0;
+    ctx = args = rtn = null;
+    call = function() {
+      timeoutID = 0;
+      last = +new Date();
+      rtn = func.apply(ctx, args);
+      ctx = null;
+      return args = null;
+    };
+    throttled = function() {
+      var delta;
+      ctx = this;
+      args = arguments;
+      delta = new Date() - last;
+      if (!timeoutID) {
+        if (delta >= wait) {
+          call();
+        } else {
+          timeoutID = setTimeout(call, wait - delta);
+        }
+      }
+      return rtn;
+    };
+    throttled.clear = function() {
+      if (!timeoutID) {
+        return;
+      }
+      clearTimeout(timeoutID);
+      return call();
+    };
+    return throttled;
+  };
+
+  Util.prototype.formatHTML = function(html) {
+    var cursor, indentString, lastMatch, level, match, re, repeatString, result, str;
+    re = /<(\/?)(.+?)(\/?)>/g;
+    result = '';
+    level = 0;
+    lastMatch = null;
+    indentString = '  ';
+    repeatString = function(str, n) {
+      return new Array(n + 1).join(str);
+    };
+    while ((match = re.exec(html)) !== null) {
+      match.isBlockNode = $.inArray(match[2], this.blockNodes) > -1;
+      match.isStartTag = match[1] !== '/' && match[3] !== '/';
+      match.isEndTag = match[1] === '/' || match[3] === '/';
+      cursor = lastMatch ? lastMatch.index + lastMatch[0].length : 0;
+      if ((str = html.substring(cursor, match.index)).length > 0 && $.trim(str)) {
+        result += str;
+      }
+      if (match.isBlockNode && match.isEndTag && !match.isStartTag) {
+        level -= 1;
+      }
+      if (match.isBlockNode && match.isStartTag) {
+        if (!(lastMatch && lastMatch.isBlockNode && lastMatch.isEndTag)) {
+          result += '\n';
+        }
+        result += repeatString(indentString, level);
+      }
+      result += match[0];
+      if (match.isBlockNode && match.isEndTag) {
+        result += '\n';
+      }
+      if (match.isBlockNode && match.isStartTag) {
+        level += 1;
+      }
+      lastMatch = match;
+    }
+    return $.trim(result);
+  };
+
+  return Util;
+
+})(SimpleModule);
+
+Toolbar = (function(superClass) {
+  extend(Toolbar, superClass);
+
+  function Toolbar() {
+    return Toolbar.__super__.constructor.apply(this, arguments);
+  }
+
+  Toolbar.pluginName = 'Toolbar';
+
+  Toolbar.prototype.opts = {
+    toolbar: true,
+    toolbarFloat: true,
+    toolbarHidden: false,
+    toolbarFloatOffset: 0
+  };
+
+  Toolbar.prototype._tpl = {
+    wrapper: '<div class="simditor-toolbar"><ul></ul></div>',
+    separator: '<li><span class="separator"></span></li>'
+  };
+
+  Toolbar.prototype._init = function() {
+    var floatInitialized, initToolbarFloat, toolbarHeight;
+    this.editor = this._module;
+    if (!this.opts.toolbar) {
+      return;
+    }
+    if (!$.isArray(this.opts.toolbar)) {
+      this.opts.toolbar = ['bold', 'italic', 'underline', 'strikethrough', '|', 'ol', 'ul', 'blockquote', 'code', '|', 'link', 'image', '|', 'indent', 'outdent'];
+    }
+    this._render();
+    this.list.on('click', function(e) {
+      return false;
+    });
+    this.wrapper.on('mousedown', (function(_this) {
+      return function(e) {
+        return _this.list.find('.menu-on').removeClass('.menu-on');
+      };
+    })(this));
+    $(document).on('mousedown.simditor' + this.editor.id, (function(_this) {
+      return function(e) {
+        return _this.list.find('.menu-on').removeClass('.menu-on');
+      };
+    })(this));
+    if (!this.opts.toolbarHidden && this.opts.toolbarFloat) {
+      this.wrapper.css('top', this.opts.toolbarFloatOffset);
+      toolbarHeight = 0;
+      initToolbarFloat = (function(_this) {
+        return function() {
+          _this.wrapper.css('position', 'static');
+          _this.wrapper.width('auto');
+          _this.editor.util.reflow(_this.wrapper);
+          _this.wrapper.width(_this.wrapper.outerWidth());
+          _this.wrapper.css('left', _this.editor.util.os.mobile ? _this.wrapper.position().left : _this.wrapper.offset().left);
+          _this.wrapper.css('position', '');
+          toolbarHeight = _this.wrapper.outerHeight();
+          _this.editor.placeholderEl.css('top', toolbarHeight);
+          return true;
+        };
+      })(this);
+      floatInitialized = null;
+      $(window).on('resize.simditor-' + this.editor.id, function(e) {
+        return floatInitialized = initToolbarFloat();
+      });
+      $(window).on('scroll.simditor-' + this.editor.id, (function(_this) {
+        return function(e) {
+          var bottomEdge, scrollTop, topEdge;
+          if (!_this.wrapper.is(':visible')) {
+            return;
+          }
+          topEdge = _this.editor.wrapper.offset().top;
+          bottomEdge = topEdge + _this.editor.wrapper.outerHeight() - 80;
+          scrollTop = $(document).scrollTop() + _this.opts.toolbarFloatOffset;
+          if (scrollTop <= topEdge || scrollTop >= bottomEdge) {
+            _this.editor.wrapper.removeClass('toolbar-floating').css('padding-top', '');
+            if (_this.editor.util.os.mobile) {
+              return _this.wrapper.css('top', _this.opts.toolbarFloatOffset);
+            }
+          } else {
+            floatInitialized || (floatInitialized = initToolbarFloat());
+            _this.editor.wrapper.addClass('toolbar-floating').css('padding-top', toolbarHeight);
+            if (_this.editor.util.os.mobile) {
+              return _this.wrapper.css('top', scrollTop - topEdge + _this.opts.toolbarFloatOffset);
+            }
+          }
+        };
+      })(this));
+    }
+    this.editor.on('destroy', (function(_this) {
+      return function() {
+        return _this.buttons.length = 0;
+      };
+    })(this));
+    return $(document).on("mousedown.simditor-" + this.editor.id, (function(_this) {
+      return function(e) {
+        return _this.list.find('li.menu-on').removeClass('menu-on');
+      };
+    })(this));
+  };
+
+  Toolbar.prototype._render = function() {
+    var k, len, name, ref;
+    this.buttons = [];
+    this.wrapper = $(this._tpl.wrapper).prependTo(this.editor.wrapper);
+    this.list = this.wrapper.find('ul');
+    ref = this.opts.toolbar;
+    for (k = 0, len = ref.length; k < len; k++) {
+      name = ref[k];
+      if (name === '|') {
+        $(this._tpl.separator).appendTo(this.list);
+        continue;
+      }
+      if (!this.constructor.buttons[name]) {
+        throw new Error("simditor: invalid toolbar button " + name);
+        continue;
+      }
+      this.buttons.push(new this.constructor.buttons[name]({
+        editor: this.editor
+      }));
+    }
+    if (this.opts.toolbarHidden) {
+      return this.wrapper.hide();
+    }
+  };
+
+  Toolbar.prototype.findButton = function(name) {
+    var button;
+    button = this.list.find('.toolbar-item-' + name).data('button');
+    return button != null ? button : null;
+  };
+
+  Toolbar.addButton = function(btn) {
+    return this.buttons[btn.prototype.name] = btn;
+  };
+
+  Toolbar.buttons = {};
+
+  return Toolbar;
+
+})(SimpleModule);
+
+Indentation = (function(superClass) {
+  extend(Indentation, superClass);
+
+  function Indentation() {
+    return Indentation.__super__.constructor.apply(this, arguments);
+  }
+
+  Indentation.pluginName = 'Indentation';
+
+  Indentation.prototype.opts = {
+    tabIndent: true
+  };
+
+  Indentation.prototype._init = function() {
+    this.editor = this._module;
+    return this.editor.keystroke.add('tab', '*', (function(_this) {
+      return function(e) {
+        var codeButton;
+        codeButton = _this.editor.toolbar.findButton('code');
+        if (!(_this.opts.tabIndent || (codeButton && codeButton.active))) {
+          return;
+        }
+        return _this.indent(e.shiftKey);
+      };
+    })(this));
+  };
+
+  Indentation.prototype.indent = function(isBackward) {
+    var $blockNodes, $endNodes, $startNodes, nodes, result;
+    $startNodes = this.editor.selection.startNodes();
+    $endNodes = this.editor.selection.endNodes();
+    $blockNodes = this.editor.selection.blockNodes();
+    nodes = [];
+    $blockNodes = $blockNodes.each(function(i, node) {
+      var include, j, k, len, n;
+      include = true;
+      for (j = k = 0, len = nodes.length; k < len; j = ++k) {
+        n = nodes[j];
+        if ($.contains(node, n)) {
+          include = false;
+          break;
+        } else if ($.contains(n, node)) {
+          nodes.splice(j, 1, node);
+          include = false;
+          break;
+        }
+      }
+      if (include) {
+        return nodes.push(node);
+      }
+    });
+    $blockNodes = $(nodes);
+    result = false;
+    $blockNodes.each((function(_this) {
+      return function(i, blockEl) {
+        var r;
+        r = isBackward ? _this.outdentBlock(blockEl) : _this.indentBlock(blockEl);
+        if (r) {
+          return result = r;
+        }
+      };
+    })(this));
+    return result;
+  };
+
+  Indentation.prototype.indentBlock = function(blockEl) {
+    var $blockEl, $childList, $nextTd, $nextTr, $parentLi, $pre, $td, $tr, marginLeft, tagName;
+    $blockEl = $(blockEl);
+    if (!$blockEl.length) {
+      return;
+    }
+    if ($blockEl.is('pre')) {
+      $pre = this.editor.selection.containerNode();
+      if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) {
+        return;
+      }
+      this.indentText(this.editor.selection.range());
+    } else if ($blockEl.is('li')) {
+      $parentLi = $blockEl.prev('li');
+      if ($parentLi.length < 1) {
+        return;
+      }
+      this.editor.selection.save();
+      tagName = $blockEl.parent()[0].tagName;
+      $childList = $parentLi.children('ul, ol');
+      if ($childList.length > 0) {
+        $childList.append($blockEl);
+      } else {
+        $('<' + tagName + '/>').append($blockEl).appendTo($parentLi);
+      }
+      this.editor.selection.restore();
+    } else if ($blockEl.is('p, h1, h2, h3, h4')) {
+      marginLeft = parseInt($blockEl.css('margin-left')) || 0;
+      marginLeft = (Math.round(marginLeft / this.opts.indentWidth) + 1) * this.opts.indentWidth;
+      $blockEl.css('margin-left', marginLeft);
+    } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) {
+      $td = this.editor.selection.containerNode().closest('td, th');
+      $nextTd = $td.next('td, th');
+      if (!($nextTd.length > 0)) {
+        $tr = $td.parent('tr');
+        $nextTr = $tr.next('tr');
+        if ($nextTr.length < 1 && $tr.parent().is('thead')) {
+          $nextTr = $tr.parent('thead').next('tbody').find('tr:first');
+        }
+        $nextTd = $nextTr.find('td:first, th:first');
+      }
+      if (!($td.length > 0 && $nextTd.length > 0)) {
+        return;
+      }
+      this.editor.selection.setRangeAtEndOf($nextTd);
+    } else {
+      return false;
+    }
+    return true;
+  };
+
+  Indentation.prototype.indentText = function(range) {
+    var text, textNode;
+    text = range.toString().replace(/^(?=.+)/mg, '\u00A0\u00A0');
+    textNode = document.createTextNode(text || '\u00A0\u00A0');
+    range.deleteContents();
+    range.insertNode(textNode);
+    if (text) {
+      range.selectNode(textNode);
+      return this.editor.selection.range(range);
+    } else {
+      return this.editor.selection.setRangeAfter(textNode);
+    }
+  };
+
+  Indentation.prototype.outdentBlock = function(blockEl) {
+    var $blockEl, $parent, $parentLi, $pre, $prevTd, $prevTr, $td, $tr, marginLeft, range;
+    $blockEl = $(blockEl);
+    if (!($blockEl && $blockEl.length > 0)) {
+      return;
+    }
+    if ($blockEl.is('pre')) {
+      $pre = this.editor.selection.containerNode();
+      if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) {
+        return;
+      }
+      this.outdentText(range);
+    } else if ($blockEl.is('li')) {
+      $parent = $blockEl.parent();
+      $parentLi = $parent.parent('li');
+      this.editor.selection.save();
+      if ($parentLi.length < 1) {
+        range = document.createRange();
+        range.setStartBefore($parent[0]);
+        range.setEndBefore($blockEl[0]);
+        $parent.before(range.extractContents());
+        $('<p/>').insertBefore($parent).after($blockEl.children('ul, ol')).append($blockEl.contents());
+        $blockEl.remove();
+      } else {
+        if ($blockEl.next('li').length > 0) {
+          $('<' + $parent[0].tagName + '/>').append($blockEl.nextAll('li')).appendTo($blockEl);
+        }
+        $blockEl.insertAfter($parentLi);
+        if ($parent.children('li').length < 1) {
+          $parent.remove();
+        }
+      }
+      this.editor.selection.restore();
+    } else if ($blockEl.is('p, h1, h2, h3, h4')) {
+      marginLeft = parseInt($blockEl.css('margin-left')) || 0;
+      marginLeft = Math.max(Math.round(marginLeft / this.opts.indentWidth) - 1, 0) * this.opts.indentWidth;
+      $blockEl.css('margin-left', marginLeft === 0 ? '' : marginLeft);
+    } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) {
+      $td = this.editor.selection.containerNode().closest('td, th');
+      $prevTd = $td.prev('td, th');
+      if (!($prevTd.length > 0)) {
+        $tr = $td.parent('tr');
+        $prevTr = $tr.prev('tr');
+        if ($prevTr.length < 1 && $tr.parent().is('tbody')) {
+          $prevTr = $tr.parent('tbody').prev('thead').find('tr:first');
+        }
+        $prevTd = $prevTr.find('td:last, th:last');
+      }
+      if (!($td.length > 0 && $prevTd.length > 0)) {
+        return;
+      }
+      this.editor.selection.setRangeAtEndOf($prevTd);
+    } else {
+      return false;
+    }
+    return true;
+  };
+
+  Indentation.prototype.outdentText = function(range) {};
+
+  return Indentation;
+
+})(SimpleModule);
+
+Clipboard = (function(superClass) {
+  extend(Clipboard, superClass);
+
+  function Clipboard() {
+    return Clipboard.__super__.constructor.apply(this, arguments);
+  }
+
+  Clipboard.pluginName = 'Clipboard';
+
+  Clipboard.prototype.opts = {
+    pasteImage: false,
+    cleanPaste: false
+  };
+
+  Clipboard.prototype._init = function() {
+    this.editor = this._module;
+    if (this.opts.pasteImage && typeof this.opts.pasteImage !== 'string') {
+      this.opts.pasteImage = 'inline';
+    }
+    return this.editor.body.on('paste', (function(_this) {
+      return function(e) {
+        var range;
+        if (_this.pasting || _this._pasteBin) {
+          return;
+        }
+        if (_this.editor.triggerHandler(e) === false) {
+          return false;
+        }
+        range = _this.editor.selection.deleteRangeContents();
+        if (_this.editor.body.html()) {
+          if (!range.collapsed) {
+            range.collapse(true);
+          }
+        } else {
+          _this.editor.formatter.format();
+          _this.editor.selection.setRangeAtStartOf(_this.editor.body.find('p:first'));
+        }
+        if (_this._processPasteByClipboardApi(e)) {
+          return false;
+        }
+        _this.editor.inputManager.throttledValueChanged.clear();
+        _this.editor.inputManager.throttledSelectionChanged.clear();
+        _this.editor.undoManager.throttledPushState.clear();
+        _this.editor.selection.reset();
+        _this.editor.undoManager.resetCaretPosition();
+        _this.pasting = true;
+        return _this._getPasteContent(function(pasteContent) {
+          _this._processPasteContent(pasteContent);
+          _this._pasteInBlockEl = null;
+          _this._pastePlainText = null;
+          return _this.pasting = false;
+        });
+      };
+    })(this));
+  };
+
+  Clipboard.prototype._processPasteByClipboardApi = function(e) {
+    var imageFile, pasteItem, ref, uploadOpt;
+    if (this.editor.util.browser.edge) {
+      return;
+    }
+    if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.items && e.originalEvent.clipboardData.items.length > 0) {
+      pasteItem = e.originalEvent.clipboardData.items[0];
+      if (/^image\//.test(pasteItem.type)) {
+        imageFile = pasteItem.getAsFile();
+        if (!((imageFile != null) && this.opts.pasteImage)) {
+          return;
+        }
+        if (!imageFile.name) {
+          imageFile.name = "Clipboard Image.png";
+        }
+        if (this.editor.triggerHandler('pasting', [imageFile]) === false) {
+          return;
+        }
+        uploadOpt = {};
+        uploadOpt[this.opts.pasteImage] = true;
+        if ((ref = this.editor.uploader) != null) {
+          ref.upload(imageFile, uploadOpt);
+        }
+        return true;
+      }
+    }
+  };
+
+  Clipboard.prototype._getPasteContent = function(callback) {
+    var state;
+    this._pasteBin = $('<div contenteditable="true" />').addClass('simditor-paste-bin').attr('tabIndex', '-1').appendTo(this.editor.el);
+    state = {
+      html: this.editor.body.html(),
+      caret: this.editor.undoManager.caretPosition()
+    };
+    this._pasteBin.focus();
+    return setTimeout((function(_this) {
+      return function() {
+        var pasteContent;
+        _this.editor.hidePopover();
+        _this.editor.body.get(0).innerHTML = state.html;
+        _this.editor.undoManager.caretPosition(state.caret);
+        _this.editor.body.focus();
+        _this.editor.selection.reset();
+        _this.editor.selection.range();
+        _this._pasteInBlockEl = _this.editor.selection.blockNodes().last();
+        _this._pastePlainText = _this.opts.cleanPaste || _this._pasteInBlockEl.is('pre, table');
+        if (_this._pastePlainText) {
+          pasteContent = _this.editor.formatter.clearHtml(_this._pasteBin.html(), true);
+        } else {
+          pasteContent = $('<div/>').append(_this._pasteBin.contents());
+          pasteContent.find('style').remove();
+          pasteContent.find('table colgroup').remove();
+          _this.editor.formatter.format(pasteContent);
+          _this.editor.formatter.decorate(pasteContent);
+          _this.editor.formatter.beautify(pasteContent.children());
+          pasteContent = pasteContent.contents();
+        }
+        _this._pasteBin.remove();
+        _this._pasteBin = null;
+        return callback(pasteContent);
+      };
+    })(this), 0);
+  };
+
+  Clipboard.prototype._processPasteContent = function(pasteContent) {
+    var $blockEl, $img, blob, children, dataURLtoBlob, img, insertPosition, k, l, lastLine, len, len1, len2, len3, len4, line, lines, m, node, o, q, ref, ref1, ref2, uploadOpt, uploader;
+    if (this.editor.triggerHandler('pasting', [pasteContent]) === false) {
+      return;
+    }
+    $blockEl = this._pasteInBlockEl;
+    if (!pasteContent) {
+      return;
+    } else if (this._pastePlainText) {
+      if ($blockEl.is('table')) {
+        lines = pasteContent.split('\n');
+        lastLine = lines.pop();
+        for (k = 0, len = lines.length; k < len; k++) {
+          line = lines[k];
+          this.editor.selection.insertNode(document.createTextNode(line));
+          this.editor.selection.insertNode($('<br/>'));
+        }
+        this.editor.selection.insertNode(document.createTextNode(lastLine));
+      } else {
+        pasteContent = $('<div/>').text(pasteContent);
+        ref = pasteContent.contents();
+        for (l = 0, len1 = ref.length; l < len1; l++) {
+          node = ref[l];
+          this.editor.selection.insertNode($(node)[0]);
+        }
+      }
+    } else if ($blockEl.is(this.editor.body)) {
+      for (m = 0, len2 = pasteContent.length; m < len2; m++) {
+        node = pasteContent[m];
+        this.editor.selection.insertNode(node);
+      }
+    } else if (pasteContent.length < 1) {
+      return;
+    } else if (pasteContent.length === 1) {
+      if (pasteContent.is('p')) {
+        children = pasteContent.contents();
+        if ($blockEl.is('h1, h2, h3, h4, h5')) {
+          if (children.length) {
+            children.css('font-size', '');
+          }
+        }
+        if (children.length === 1 && children.is('img')) {
+          $img = children;
+          if (/^data:image/.test($img.attr('src'))) {
+            if (!this.opts.pasteImage) {
+              return;
+            }
+            blob = this.editor.util.dataURLtoBlob($img.attr("src"));
+            blob.name = "Clipboard Image.png";
+            uploadOpt = {};
+            uploadOpt[this.opts.pasteImage] = true;
+            if ((ref1 = this.editor.uploader) != null) {
+              ref1.upload(blob, uploadOpt);
+            }
+            return;
+          } else if (new RegExp('^blob:' + location.origin + '/').test($img.attr('src'))) {
+            if (!this.opts.pasteImage) {
+              return;
+            }
+            uploadOpt = {};
+            uploadOpt[this.opts.pasteImage] = true;
+            dataURLtoBlob = this.editor.util.dataURLtoBlob;
+            uploader = this.editor.uploader;
+            img = new Image;
+            img.onload = function() {
+              var canvas;
+              canvas = document.createElement('canvas');
+              canvas.width = img.naturalWidth;
+              canvas.height = img.naturalHeight;
+              canvas.getContext('2d').drawImage(img, 0, 0);
+              blob = dataURLtoBlob(canvas.toDataURL('image/png'));
+              blob.name = 'Clipboard Image.png';
+              if (uploader !== null) {
+                uploader.upload(blob, uploadOpt);
+              }
+            };
+            img.src = $img.attr('src');
+            return;
+          } else if ($img.is('img[src^="webkit-fake-url://"]')) {
+            return;
+          }
+        }
+        for (o = 0, len3 = children.length; o < len3; o++) {
+          node = children[o];
+          this.editor.selection.insertNode(node);
+        }
+      } else if ($blockEl.is('p') && this.editor.util.isEmptyNode($blockEl)) {
+        $blockEl.replaceWith(pasteContent);
+        this.editor.selection.setRangeAtEndOf(pasteContent);
+      } else if (pasteContent.is('ul, ol')) {
+        if (pasteContent.find('li').length === 1) {
+          pasteContent = $('<div/>').text(pasteContent.text());
+          ref2 = pasteContent.contents();
+          for (q = 0, len4 = ref2.length; q < len4; q++) {
+            node = ref2[q];
+            this.editor.selection.insertNode($(node)[0]);
+          }
+        } else if ($blockEl.is('li')) {
+          $blockEl.parent().after(pasteContent);
+          this.editor.selection.setRangeAtEndOf(pasteContent);
+        } else {
+          $blockEl.after(pasteContent);
+          this.editor.selection.setRangeAtEndOf(pasteContent);
+        }
+      } else {
+        $blockEl.after(pasteContent);
+        this.editor.selection.setRangeAtEndOf(pasteContent);
+      }
+    } else {
+      if ($blockEl.is('li')) {
+        $blockEl = $blockEl.parent();
+      }
+      if (this.editor.selection.rangeAtStartOf($blockEl)) {
+        insertPosition = 'before';
+      } else if (this.editor.selection.rangeAtEndOf($blockEl)) {
+        insertPosition = 'after';
+      } else {
+        this.editor.selection.breakBlockEl($blockEl);
+        insertPosition = 'before';
+      }
+      $blockEl[insertPosition](pasteContent);
+      this.editor.selection.setRangeAtEndOf(pasteContent.last());
+    }
+    return this.editor.inputManager.throttledValueChanged();
+  };
+
+  return Clipboard;
+
+})(SimpleModule);
+
+Simditor = (function(superClass) {
+  extend(Simditor, superClass);
+
+  function Simditor() {
+    return Simditor.__super__.constructor.apply(this, arguments);
+  }
+
+  Simditor.connect(Util);
+
+  Simditor.connect(InputManager);
+
+  Simditor.connect(Selection);
+
+  Simditor.connect(UndoManager);
+
+  Simditor.connect(Keystroke);
+
+  Simditor.connect(Formatter);
+
+  Simditor.connect(Toolbar);
+
+  Simditor.connect(Indentation);
+
+  Simditor.connect(Clipboard);
+
+  Simditor.count = 0;
+
+  Simditor.prototype.opts = {
+    textarea: null,
+    placeholder: '',
+    defaultImage: 'images/image.png',
+    params: {},
+    upload: false,
+    indentWidth: 40
+  };
+
+  Simditor.prototype._init = function() {
+    var e, editor, uploadOpts;
+    this.textarea = $(this.opts.textarea);
+    this.opts.placeholder = this.opts.placeholder || this.textarea.attr('placeholder');
+    if (!this.textarea.length) {
+      throw new Error('simditor: param textarea is required.');
+      return;
+    }
+    editor = this.textarea.data('simditor');
+    if (editor != null) {
+      editor.destroy();
+    }
+    this.id = ++Simditor.count;
+    this._render();
+    if (simpleHotkeys) {
+      this.hotkeys = simpleHotkeys({
+        el: this.body
+      });
+    } else {
+      throw new Error('simditor: simple-hotkeys is required.');
+      return;
+    }
+    if (this.opts.upload && simpleUploader) {
+      uploadOpts = typeof this.opts.upload === 'object' ? this.opts.upload : {};
+      this.uploader = simpleUploader(uploadOpts);
+    }
+    this.on('initialized', (function(_this) {
+      return function() {
+        if (_this.opts.placeholder) {
+          _this.on('valuechanged', function() {
+            return _this._placeholder();
+          });
+        }
+        _this.setValue(_this.textarea.val().trim() || '');
+        if (_this.textarea.attr('autofocus')) {
+          return _this.focus();
+        }
+      };
+    })(this));
+    if (this.util.browser.mozilla) {
+      this.util.reflow();
+      try {
+        document.execCommand('enableObjectResizing', false, false);
+        return document.execCommand('enableInlineTableEditing', false, false);
+      } catch (_error) {
+        e = _error;
+      }
+    }
+  };
+
+  Simditor.prototype._tpl = "<div class=\"simditor\">\n  <div class=\"simditor-wrapper\">\n    <div class=\"simditor-placeholder\"></div>\n    <div class=\"simditor-body\" contenteditable=\"true\">\n    </div>\n  </div>\n</div>";
+
+  Simditor.prototype._render = function() {
+    var key, ref, results, val;
+    this.el = $(this._tpl).insertBefore(this.textarea);
+    this.wrapper = this.el.find('.simditor-wrapper');
+    this.body = this.wrapper.find('.simditor-body');
+    this.placeholderEl = this.wrapper.find('.simditor-placeholder').append(this.opts.placeholder);
+    this.el.data('simditor', this);
+    this.wrapper.append(this.textarea);
+    this.textarea.data('simditor', this).blur();
+    this.body.attr('tabindex', this.textarea.attr('tabindex'));
+    if (this.util.os.mac) {
+      this.el.addClass('simditor-mac');
+    } else if (this.util.os.linux) {
+      this.el.addClass('simditor-linux');
+    }
+    if (this.util.os.mobile) {
+      this.el.addClass('simditor-mobile');
+    }
+    if (this.opts.params) {
+      ref = this.opts.params;
+      results = [];
+      for (key in ref) {
+        val = ref[key];
+        results.push($('<input/>', {
+          type: 'hidden',
+          name: key,
+          value: val
+        }).insertAfter(this.textarea));
+      }
+      return results;
+    }
+  };
+
+  Simditor.prototype._placeholder = function() {
+    var children;
+    children = this.body.children();
+    if (children.length === 0 || (children.length === 1 && this.util.isEmptyNode(children) && parseInt(children.css('margin-left') || 0) < this.opts.indentWidth)) {
+      return this.placeholderEl.show();
+    } else {
+      return this.placeholderEl.hide();
+    }
+  };
+
+  Simditor.prototype.setValue = function(val) {
+    this.hidePopover();
+    this.textarea.val(val);
+    this.body.get(0).innerHTML = val;
+    this.formatter.format();
+    this.formatter.decorate();
+    this.util.reflow(this.body);
+    this.inputManager.lastCaretPosition = null;
+    return this.trigger('valuechanged');
+  };
+
+  Simditor.prototype.getValue = function() {
+    return this.sync();
+  };
+
+  Simditor.prototype.sync = function() {
+    var children, cloneBody, emptyP, firstP, lastP, val;
+    cloneBody = this.body.clone();
+    this.formatter.undecorate(cloneBody);
+    this.formatter.format(cloneBody);
+    this.formatter.autolink(cloneBody);
+    children = cloneBody.children();
+    lastP = children.last('p');
+    firstP = children.first('p');
+    while (lastP.is('p') && this.util.isEmptyNode(lastP)) {
+      emptyP = lastP;
+      lastP = lastP.prev('p');
+      emptyP.remove();
+    }
+    while (firstP.is('p') && this.util.isEmptyNode(firstP)) {
+      emptyP = firstP;
+      firstP = lastP.next('p');
+      emptyP.remove();
+    }
+    cloneBody.find('img.uploading').remove();
+    val = $.trim(cloneBody.html());
+    this.textarea.val(val);
+    return val;
+  };
+
+  Simditor.prototype.focus = function() {
+    var $blockEl, range;
+    if (!(this.body.is(':visible') && this.body.is('[contenteditable]'))) {
+      this.el.find('textarea:visible').focus();
+      return;
+    }
+    if (this.inputManager.lastCaretPosition) {
+      this.undoManager.caretPosition(this.inputManager.lastCaretPosition);
+      return this.inputManager.lastCaretPosition = null;
+    } else {
+      $blockEl = this.body.children().last();
+      if (!$blockEl.is('p')) {
+        $blockEl = $('<p/>').append(this.util.phBr).appendTo(this.body);
+      }
+      range = document.createRange();
+      return this.selection.setRangeAtEndOf($blockEl, range);
+    }
+  };
+
+  Simditor.prototype.blur = function() {
+    if (this.body.is(':visible') && this.body.is('[contenteditable]')) {
+      return this.body.blur();
+    } else {
+      return this.body.find('textarea:visible').blur();
+    }
+  };
+
+  Simditor.prototype.hidePopover = function() {
+    return this.el.find('.simditor-popover').each(function(i, popover) {
+      popover = $(popover).data('popover');
+      if (popover.active) {
+        return popover.hide();
+      }
+    });
+  };
+
+  Simditor.prototype.destroy = function() {
+    this.triggerHandler('destroy');
+    this.textarea.closest('form').off('.simditor .simditor-' + this.id);
+    this.selection.clear();
+    this.inputManager.focused = false;
+    this.textarea.insertBefore(this.el).hide().val('').removeData('simditor');
+    this.el.remove();
+    $(document).off('.simditor-' + this.id);
+    $(window).off('.simditor-' + this.id);
+    return this.off();
+  };
+
+  return Simditor;
+
+})(SimpleModule);
+
+Simditor.i18n = {
+  'zh-CN': {
+    'blockquote': '引用',
+    'bold': '加粗文字',
+    'code': '插入代码',
+    'color': '文字颜色',
+    'coloredText': '彩色文字',
+    'hr': '分隔线',
+    'image': '插入图片',
+    'externalImage': '外链图片',
+    'selectImage': '选择图片',
+    'uploadImage': '上传图片',
+    'uploadFailed': '上传失败了',
+    'uploadError': '上传出错了',
+    'imageUrl': '图片地址',
+    'imageSize': '图片尺寸',
+    'imageAlt': '图片描述',
+    'restoreImageSize': '还原图片尺寸',
+    'uploading': '正在上传',
+    'indent': '向右缩进',
+    'outdent': '向左缩进',
+    'italic': '斜体文字',
+    'link': '插入链接',
+    'linkText': '链接文字',
+    'linkUrl': '链接地址',
+    'linkTarget': '打开方式',
+    'openLinkInCurrentWindow': '在当前窗口中打开',
+    'openLinkInNewWindow': '在新窗口中打开',
+    'removeLink': '移除链接',
+    'ol': '有序列表',
+    'ul': '无序列表',
+    'strikethrough': '删除线文字',
+    'table': '表格',
+    'deleteRow': '删除行',
+    'insertRowAbove': '在上面插入行',
+    'insertRowBelow': '在下面插入行',
+    'deleteColumn': '删除列',
+    'insertColumnLeft': '在左边插入列',
+    'insertColumnRight': '在右边插入列',
+    'deleteTable': '删除表格',
+    'title': '标题',
+    'normalText': '普通文本',
+    'underline': '下划线文字',
+    'alignment': '水平对齐',
+    'alignCenter': '居中',
+    'alignLeft': '居左',
+    'alignRight': '居右',
+    'selectLanguage': '选择程序语言',
+    'fontScale': '字体大小',
+    'fontScaleXLarge': '超大字体',
+    'fontScaleLarge': '大号字体',
+    'fontScaleNormal': '正常大小',
+    'fontScaleSmall': '小号字体',
+    'fontScaleXSmall': '超小字体'
+  },
+  'en-US': {
+    'blockquote': 'Block Quote',
+    'bold': 'Bold',
+    'code': 'Code',
+    'color': 'Text Color',
+    'coloredText': 'Colored Text',
+    'hr': 'Horizontal Line',
+    'image': 'Insert Image',
+    'externalImage': 'External Image',
+    'selectImage': 'Select Image',
+    'uploadImage': 'Upload Image',
+    'uploadFailed': 'Upload failed',
+    'uploadError': 'Error occurs during upload',
+    'imageUrl': 'Url',
+    'imageSize': 'Size',
+    'imageAlt': 'Alt',
+    'restoreImageSize': 'Restore Origin Size',
+    'uploading': 'Uploading',
+    'indent': 'Indent',
+    'outdent': 'Outdent',
+    'italic': 'Italic',
+    'link': 'Insert Link',
+    'linkText': 'Text',
+    'linkUrl': 'Url',
+    'linkTarget': 'Target',
+    'openLinkInCurrentWindow': 'Open link in current window',
+    'openLinkInNewWindow': 'Open link in new window',
+    'removeLink': 'Remove Link',
+    'ol': 'Ordered List',
+    'ul': 'Unordered List',
+    'strikethrough': 'Strikethrough',
+    'table': 'Table',
+    'deleteRow': 'Delete Row',
+    'insertRowAbove': 'Insert Row Above',
+    'insertRowBelow': 'Insert Row Below',
+    'deleteColumn': 'Delete Column',
+    'insertColumnLeft': 'Insert Column Left',
+    'insertColumnRight': 'Insert Column Right',
+    'deleteTable': 'Delete Table',
+    'title': 'Title',
+    'normalText': 'Text',
+    'underline': 'Underline',
+    'alignment': 'Alignment',
+    'alignCenter': 'Align Center',
+    'alignLeft': 'Align Left',
+    'alignRight': 'Align Right',
+    'selectLanguage': 'Select Language',
+    'fontScale': 'Font Size',
+    'fontScaleXLarge': 'X Large Size',
+    'fontScaleLarge': 'Large Size',
+    'fontScaleNormal': 'Normal Size',
+    'fontScaleSmall': 'Small Size',
+    'fontScaleXSmall': 'X Small Size'
+  }
+};
+
+Button = (function(superClass) {
+  extend(Button, superClass);
+
+  Button.prototype._tpl = {
+    item: '<li><a tabindex="-1" unselectable="on" class="toolbar-item" href="javascript:;"><span></span></a></li>',
+    menuWrapper: '<div class="toolbar-menu"></div>',
+    menuItem: '<li><a tabindex="-1" unselectable="on" class="menu-item" href="javascript:;"><span></span></a></li>',
+    separator: '<li><span class="separator"></span></li>'
+  };
+
+  Button.prototype.name = '';
+
+  Button.prototype.icon = '';
+
+  Button.prototype.title = '';
+
+  Button.prototype.text = '';
+
+  Button.prototype.htmlTag = '';
+
+  Button.prototype.disableTag = '';
+
+  Button.prototype.menu = false;
+
+  Button.prototype.active = false;
+
+  Button.prototype.disabled = false;
+
+  Button.prototype.needFocus = true;
+
+  Button.prototype.shortcut = null;
+
+  function Button(opts) {
+    this.editor = opts.editor;
+    this.title = this._t(this.name);
+    Button.__super__.constructor.call(this, opts);
+  }
+
+  Button.prototype._init = function() {
+    var k, len, ref, tag;
+    this.render();
+    this.el.on('mousedown', (function(_this) {
+      return function(e) {
+        var exceed, noFocus, param;
+        e.preventDefault();
+        noFocus = _this.needFocus && !_this.editor.inputManager.focused;
+        if (_this.el.hasClass('disabled')) {
+          return false;
+        }
+        if (noFocus) {
+          _this.editor.focus();
+        }
+        if (_this.menu) {
+          _this.wrapper.toggleClass('menu-on').siblings('li').removeClass('menu-on');
+          if (_this.wrapper.is('.menu-on')) {
+            exceed = _this.menuWrapper.offset().left + _this.menuWrapper.outerWidth() + 5 - _this.editor.wrapper.offset().left - _this.editor.wrapper.outerWidth();
+            if (exceed > 0) {
+              _this.menuWrapper.css({
+                'left': 'auto',
+                'right': 0
+              });
+            }
+            _this.trigger('menuexpand');
+          }
+          return false;
+        }
+        param = _this.el.data('param');
+        _this.command(param);
+        return false;
+      };
+    })(this));
+    this.wrapper.on('click', 'a.menu-item', (function(_this) {
+      return function(e) {
+        var btn, noFocus, param;
+        e.preventDefault();
+        btn = $(e.currentTarget);
+        _this.wrapper.removeClass('menu-on');
+        noFocus = _this.needFocus && !_this.editor.inputManager.focused;
+        if (btn.hasClass('disabled') || noFocus) {
+          return false;
+        }
+        _this.editor.toolbar.wrapper.removeClass('menu-on');
+        param = btn.data('param');
+        if(btn.hasClass("menu-item-select-image")){
+            parent.Fast.api.open("general/attachment/select?element_id=&multiple=true&mimetype=image/*", "选择", {
+                callback: function (data) {
+                    var urlArr = data.url.split(/\,/);
+                    $.each(urlArr, function () {
+                        var url = Fast.api.cdnurl(this);
+                        _this.command(url);
+                    });
+                }
+            });
+            return false;
+        }else{
+          _this.command(param);
+        }
+        return false;
+      };
+    })(this));
+    this.wrapper.on('mousedown', 'a.menu-item', function(e) {
+      return false;
+    });
+    this.editor.on('blur', (function(_this) {
+      return function() {
+        var editorActive;
+        editorActive = _this.editor.body.is(':visible') && _this.editor.body.is('[contenteditable]');
+        if (!(editorActive && !_this.editor.clipboard.pasting)) {
+          return;
+        }
+        _this.setActive(false);
+        return _this.setDisabled(false);
+      };
+    })(this));
+    if (this.shortcut != null) {
+      this.editor.hotkeys.add(this.shortcut, (function(_this) {
+        return function(e) {
+          _this.el.mousedown();
+          return false;
+        };
+      })(this));
+    }
+    ref = this.htmlTag.split(',');
+    for (k = 0, len = ref.length; k < len; k++) {
+      tag = ref[k];
+      tag = $.trim(tag);
+      if (tag && $.inArray(tag, this.editor.formatter._allowedTags) < 0) {
+        this.editor.formatter._allowedTags.push(tag);
+      }
+    }
+    return this.editor.on('selectionchanged', (function(_this) {
+      return function(e) {
+        if (_this.editor.inputManager.focused) {
+          return _this._status();
+        }
+      };
+    })(this));
+  };
+
+  Button.prototype.iconClassOf = function(icon) {
+    if (icon) {
+      return "simditor-icon simditor-icon-" + icon;
+    } else {
+      return '';
+    }
+  };
+
+  Button.prototype.setIcon = function(icon) {
+    return this.el.find('span').removeClass().addClass(this.iconClassOf(icon)).text(this.text);
+  };
+
+  Button.prototype.render = function() {
+    this.wrapper = $(this._tpl.item).appendTo(this.editor.toolbar.list);
+    this.el = this.wrapper.find('a.toolbar-item');
+    this.el.attr('title', this.title).addClass("toolbar-item-" + this.name).data('button', this);
+    this.setIcon(this.icon);
+    if (!this.menu) {
+      return;
+    }
+    this.menuWrapper = $(this._tpl.menuWrapper).appendTo(this.wrapper);
+    this.menuWrapper.addClass("toolbar-menu-" + this.name);
+    return this.renderMenu();
+  };
+
+  Button.prototype.renderMenu = function() {
+    var $menuBtnEl, $menuItemEl, k, len, menuItem, ref, ref1, results;
+    if (!$.isArray(this.menu)) {
+      return;
+    }
+    this.menuEl = $('<ul/>').appendTo(this.menuWrapper);
+    ref = this.menu;
+    results = [];
+    for (k = 0, len = ref.length; k < len; k++) {
+      menuItem = ref[k];
+      if (menuItem === '|') {
+        $(this._tpl.separator).appendTo(this.menuEl);
+        continue;
+      }
+      $menuItemEl = $(this._tpl.menuItem).appendTo(this.menuEl);
+      $menuBtnEl = $menuItemEl.find('a.menu-item').attr({
+        'title': (ref1 = menuItem.title) != null ? ref1 : menuItem.text,
+        'data-param': menuItem.param
+      }).addClass('menu-item-' + menuItem.name);
+      if (menuItem.icon) {
+        results.push($menuBtnEl.find('span').addClass(this.iconClassOf(menuItem.icon)));
+      } else {
+        results.push($menuBtnEl.find('span').text(menuItem.text));
+      }
+    }
+    return results;
+  };
+
+  Button.prototype.setActive = function(active) {
+    if (active === this.active) {
+      return;
+    }
+    this.active = active;
+    return this.el.toggleClass('active', this.active);
+  };
+
+  Button.prototype.setDisabled = function(disabled) {
+    if (disabled === this.disabled) {
+      return;
+    }
+    this.disabled = disabled;
+    return this.el.toggleClass('disabled', this.disabled);
+  };
+
+  Button.prototype._disableStatus = function() {
+    var disabled, endNodes, startNodes;
+    startNodes = this.editor.selection.startNodes();
+    endNodes = this.editor.selection.endNodes();
+    disabled = startNodes.filter(this.disableTag).length > 0 || endNodes.filter(this.disableTag).length > 0;
+    this.setDisabled(disabled);
+    if (this.disabled) {
+      this.setActive(false);
+    }
+    return this.disabled;
+  };
+
+  Button.prototype._activeStatus = function() {
+    var active, endNode, endNodes, startNode, startNodes;
+    startNodes = this.editor.selection.startNodes();
+    endNodes = this.editor.selection.endNodes();
+    startNode = startNodes.filter(this.htmlTag);
+    endNode = endNodes.filter(this.htmlTag);
+    active = startNode.length > 0 && endNode.length > 0 && startNode.is(endNode);
+    this.node = active ? startNode : null;
+    this.setActive(active);
+    return this.active;
+  };
+
+  Button.prototype._status = function() {
+    this._disableStatus();
+    if (this.disabled) {
+      return;
+    }
+    return this._activeStatus();
+  };
+
+  Button.prototype.command = function(param) {};
+
+  Button.prototype._t = function() {
+    var args, ref, result;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    result = Button.__super__._t.apply(this, args);
+    if (!result) {
+      result = (ref = this.editor)._t.apply(ref, args);
+    }
+    return result;
+  };
+
+  return Button;
+
+})(SimpleModule);
+
+Simditor.Button = Button;
+
+Popover = (function(superClass) {
+  extend(Popover, superClass);
+
+  Popover.prototype.offset = {
+    top: 4,
+    left: 0
+  };
+
+  Popover.prototype.target = null;
+
+  Popover.prototype.active = false;
+
+  function Popover(opts) {
+    this.button = opts.button;
+    this.editor = opts.button.editor;
+    Popover.__super__.constructor.call(this, opts);
+  }
+
+  Popover.prototype._init = function() {
+    this.el = $('<div class="simditor-popover"></div>').appendTo(this.editor.el).data('popover', this);
+    this.render();
+    this.el.on('mouseenter', (function(_this) {
+      return function(e) {
+        return _this.el.addClass('hover');
+      };
+    })(this));
+    return this.el.on('mouseleave', (function(_this) {
+      return function(e) {
+        return _this.el.removeClass('hover');
+      };
+    })(this));
+  };
+
+  Popover.prototype.render = function() {};
+
+  Popover.prototype._initLabelWidth = function() {
+    var $fields;
+    $fields = this.el.find('.settings-field');
+    if (!($fields.length > 0)) {
+      return;
+    }
+    this._labelWidth = 0;
+    $fields.each((function(_this) {
+      return function(i, field) {
+        var $field, $label;
+        $field = $(field);
+        $label = $field.find('label');
+        if (!($label.length > 0)) {
+          return;
+        }
+        return _this._labelWidth = Math.max(_this._labelWidth, $label.width());
+      };
+    })(this));
+    return $fields.find('label').width(this._labelWidth);
+  };
+
+  Popover.prototype.show = function($target, position) {
+    if (position == null) {
+      position = 'bottom';
+    }
+    if ($target == null) {
+      return;
+    }
+    this.el.siblings('.simditor-popover').each(function(i, popover) {
+      popover = $(popover).data('popover');
+      if (popover.active) {
+        return popover.hide();
+      }
+    });
+    if (this.active && this.target) {
+      this.target.removeClass('selected');
+    }
+    this.target = $target.addClass('selected');
+    if (this.active) {
+      this.refresh(position);
+      return this.trigger('popovershow');
+    } else {
+      this.active = true;
+      this.el.css({
+        left: -9999
+      }).show();
+      if (!this._labelWidth) {
+        this._initLabelWidth();
+      }
+      this.editor.util.reflow();
+      this.refresh(position);
+      return this.trigger('popovershow');
+    }
+  };
+
+  Popover.prototype.hide = function() {
+    if (!this.active) {
+      return;
+    }
+    if (this.target) {
+      this.target.removeClass('selected');
+    }
+    this.target = null;
+    this.active = false;
+    this.el.hide();
+    return this.trigger('popoverhide');
+  };
+
+  Popover.prototype.refresh = function(position) {
+    var editorOffset, left, maxLeft, targetH, targetOffset, top;
+    if (position == null) {
+      position = 'bottom';
+    }
+    if (!this.active) {
+      return;
+    }
+    editorOffset = this.editor.el.offset();
+    targetOffset = this.target.offset();
+    targetH = this.target.outerHeight();
+    if (position === 'bottom') {
+      top = targetOffset.top - editorOffset.top + targetH;
+    } else if (position === 'top') {
+      top = targetOffset.top - editorOffset.top - this.el.height();
+    }
+    maxLeft = this.editor.wrapper.width() - this.el.outerWidth() - 10;
+    left = Math.min(targetOffset.left - editorOffset.left, maxLeft);
+    return this.el.css({
+      top: top + this.offset.top,
+      left: left + this.offset.left
+    });
+  };
+
+  Popover.prototype.destroy = function() {
+    this.target = null;
+    this.active = false;
+    this.editor.off('.linkpopover');
+    return this.el.remove();
+  };
+
+  Popover.prototype._t = function() {
+    var args, ref, result;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    result = Popover.__super__._t.apply(this, args);
+    if (!result) {
+      result = (ref = this.button)._t.apply(ref, args);
+    }
+    return result;
+  };
+
+  return Popover;
+
+})(SimpleModule);
+
+Simditor.Popover = Popover;
+
+TitleButton = (function(superClass) {
+  extend(TitleButton, superClass);
+
+  function TitleButton() {
+    return TitleButton.__super__.constructor.apply(this, arguments);
+  }
+
+  TitleButton.prototype.name = 'title';
+
+  TitleButton.prototype.htmlTag = 'h1, h2, h3, h4, h5';
+
+  TitleButton.prototype.disableTag = 'pre, table';
+
+  TitleButton.prototype._init = function() {
+    this.menu = [
+      {
+        name: 'normal',
+        text: this._t('normalText'),
+        param: 'p'
+      }, '|', {
+        name: 'h1',
+        text: this._t('title') + ' 1',
+        param: 'h1'
+      }, {
+        name: 'h2',
+        text: this._t('title') + ' 2',
+        param: 'h2'
+      }, {
+        name: 'h3',
+        text: this._t('title') + ' 3',
+        param: 'h3'
+      }, {
+        name: 'h4',
+        text: this._t('title') + ' 4',
+        param: 'h4'
+      }, {
+        name: 'h5',
+        text: this._t('title') + ' 5',
+        param: 'h5'
+      }
+    ];
+    return TitleButton.__super__._init.call(this);
+  };
+
+  TitleButton.prototype.setActive = function(active, param) {
+    TitleButton.__super__.setActive.call(this, active);
+    if (active) {
+      param || (param = this.node[0].tagName.toLowerCase());
+    }
+    this.el.removeClass('active-p active-h1 active-h2 active-h3 active-h4 active-h5');
+    if (active) {
+      return this.el.addClass('active active-' + param);
+    }
+  };
+
+  TitleButton.prototype.command = function(param) {
+    var $rootNodes;
+    $rootNodes = this.editor.selection.rootNodes();
+    this.editor.selection.save();
+    $rootNodes.each((function(_this) {
+      return function(i, node) {
+        var $node;
+        $node = $(node);
+        if ($node.is('blockquote') || $node.is(param) || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) {
+          return;
+        }
+        return $('<' + param + '/>').append($node.contents()).replaceAll($node);
+      };
+    })(this));
+    this.editor.selection.restore();
+    return this.editor.trigger('valuechanged');
+  };
+
+  return TitleButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(TitleButton);
+
+FontScaleButton = (function(superClass) {
+  extend(FontScaleButton, superClass);
+
+  function FontScaleButton() {
+    return FontScaleButton.__super__.constructor.apply(this, arguments);
+  }
+
+  FontScaleButton.prototype.name = 'fontScale';
+
+  FontScaleButton.prototype.icon = 'font';
+
+  FontScaleButton.prototype.htmlTag = 'span';
+
+  FontScaleButton.prototype.disableTag = 'pre, h1, h2, h3, h4, h5';
+
+  FontScaleButton.prototype.sizeMap = {
+    'x-large': '1.5em',
+    'large': '1.25em',
+    'small': '.75em',
+    'x-small': '.5em'
+  };
+
+  FontScaleButton.prototype._init = function() {
+    this.menu = [
+      {
+        name: '150%',
+        text: this._t('fontScaleXLarge'),
+        param: '5'
+      }, {
+        name: '125%',
+        text: this._t('fontScaleLarge'),
+        param: '4'
+      }, {
+        name: '100%',
+        text: this._t('fontScaleNormal'),
+        param: '3'
+      }, {
+        name: '75%',
+        text: this._t('fontScaleSmall'),
+        param: '2'
+      }, {
+        name: '50%',
+        text: this._t('fontScaleXSmall'),
+        param: '1'
+      }
+    ];
+    return FontScaleButton.__super__._init.call(this);
+  };
+
+  FontScaleButton.prototype._activeStatus = function() {
+    var active, endNode, endNodes, range, startNode, startNodes;
+    range = this.editor.selection.range();
+    startNodes = this.editor.selection.startNodes();
+    endNodes = this.editor.selection.endNodes();
+    startNode = startNodes.filter('span[style*="font-size"]');
+    endNode = endNodes.filter('span[style*="font-size"]');
+    active = startNodes.length > 0 && endNodes.length > 0 && startNode.is(endNode);
+    this.setActive(active);
+    return this.active;
+  };
+
+  FontScaleButton.prototype.command = function(param) {
+    var $scales, containerNode, range;
+    range = this.editor.selection.range();
+    if (range.collapsed) {
+      return;
+    }
+    this.editor.selection.range(range);
+    document.execCommand('styleWithCSS', false, true);
+    document.execCommand('fontSize', false, param);
+    document.execCommand('styleWithCSS', false, false);
+    this.editor.selection.reset();
+    this.editor.selection.range();
+    containerNode = this.editor.selection.containerNode();
+    if (containerNode[0].nodeType === Node.TEXT_NODE) {
+      $scales = containerNode.closest('span[style*="font-size"]');
+    } else {
+      $scales = containerNode.find('span[style*="font-size"]');
+    }
+    $scales.each((function(_this) {
+      return function(i, n) {
+        var $span, size;
+        $span = $(n);
+        size = n.style.fontSize;
+        if (/large|x-large|small|x-small/.test(size)) {
+          return $span.css('fontSize', _this.sizeMap[size]);
+        } else if (size === 'medium') {
+          return $span.replaceWith($span.contents());
+        }
+      };
+    })(this));
+    return this.editor.trigger('valuechanged');
+  };
+
+  return FontScaleButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(FontScaleButton);
+
+BoldButton = (function(superClass) {
+  extend(BoldButton, superClass);
+
+  function BoldButton() {
+    return BoldButton.__super__.constructor.apply(this, arguments);
+  }
+
+  BoldButton.prototype.name = 'bold';
+
+  BoldButton.prototype.icon = 'bold';
+
+  BoldButton.prototype.htmlTag = 'b, strong';
+
+  BoldButton.prototype.disableTag = 'pre';
+
+  BoldButton.prototype.shortcut = 'cmd+b';
+
+  BoldButton.prototype._init = function() {
+    if (this.editor.util.os.mac) {
+      this.title = this.title + ' ( Cmd + b )';
+    } else {
+      this.title = this.title + ' ( Ctrl + b )';
+      this.shortcut = 'ctrl+b';
+    }
+    return BoldButton.__super__._init.call(this);
+  };
+
+  BoldButton.prototype._activeStatus = function() {
+    var active;
+    active = document.queryCommandState('bold') === true;
+    this.setActive(active);
+    return this.active;
+  };
+
+  BoldButton.prototype.command = function() {
+    document.execCommand('bold');
+    if (!this.editor.util.support.oninput) {
+      this.editor.trigger('valuechanged');
+    }
+    return $(document).trigger('selectionchange');
+  };
+
+  return BoldButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(BoldButton);
+
+ItalicButton = (function(superClass) {
+  extend(ItalicButton, superClass);
+
+  function ItalicButton() {
+    return ItalicButton.__super__.constructor.apply(this, arguments);
+  }
+
+  ItalicButton.prototype.name = 'italic';
+
+  ItalicButton.prototype.icon = 'italic';
+
+  ItalicButton.prototype.htmlTag = 'i';
+
+  ItalicButton.prototype.disableTag = 'pre';
+
+  ItalicButton.prototype.shortcut = 'cmd+i';
+
+  ItalicButton.prototype._init = function() {
+    if (this.editor.util.os.mac) {
+      this.title = this.title + " ( Cmd + i )";
+    } else {
+      this.title = this.title + " ( Ctrl + i )";
+      this.shortcut = 'ctrl+i';
+    }
+    return ItalicButton.__super__._init.call(this);
+  };
+
+  ItalicButton.prototype._activeStatus = function() {
+    var active;
+    active = document.queryCommandState('italic') === true;
+    this.setActive(active);
+    return this.active;
+  };
+
+  ItalicButton.prototype.command = function() {
+    document.execCommand('italic');
+    if (!this.editor.util.support.oninput) {
+      this.editor.trigger('valuechanged');
+    }
+    return $(document).trigger('selectionchange');
+  };
+
+  return ItalicButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(ItalicButton);
+
+UnderlineButton = (function(superClass) {
+  extend(UnderlineButton, superClass);
+
+  function UnderlineButton() {
+    return UnderlineButton.__super__.constructor.apply(this, arguments);
+  }
+
+  UnderlineButton.prototype.name = 'underline';
+
+  UnderlineButton.prototype.icon = 'underline';
+
+  UnderlineButton.prototype.htmlTag = 'u';
+
+  UnderlineButton.prototype.disableTag = 'pre';
+
+  UnderlineButton.prototype.shortcut = 'cmd+u';
+
+  UnderlineButton.prototype.render = function() {
+    if (this.editor.util.os.mac) {
+      this.title = this.title + ' ( Cmd + u )';
+    } else {
+      this.title = this.title + ' ( Ctrl + u )';
+      this.shortcut = 'ctrl+u';
+    }
+    return UnderlineButton.__super__.render.call(this);
+  };
+
+  UnderlineButton.prototype._activeStatus = function() {
+    var active;
+    active = document.queryCommandState('underline') === true;
+    this.setActive(active);
+    return this.active;
+  };
+
+  UnderlineButton.prototype.command = function() {
+    document.execCommand('underline');
+    if (!this.editor.util.support.oninput) {
+      this.editor.trigger('valuechanged');
+    }
+    return $(document).trigger('selectionchange');
+  };
+
+  return UnderlineButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(UnderlineButton);
+
+ColorButton = (function(superClass) {
+  extend(ColorButton, superClass);
+
+  function ColorButton() {
+    return ColorButton.__super__.constructor.apply(this, arguments);
+  }
+
+  ColorButton.prototype.name = 'color';
+
+  ColorButton.prototype.icon = 'tint';
+
+  ColorButton.prototype.disableTag = 'pre';
+
+  ColorButton.prototype.menu = true;
+
+  ColorButton.prototype.render = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    return ColorButton.__super__.render.apply(this, args);
+  };
+
+  ColorButton.prototype.renderMenu = function() {
+    $('<ul class="color-list">\n  <li><a href="javascript:;" class="font-color font-color-1"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-2"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-3"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-4"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-5"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-6"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-7"></a></li>\n  <li><a href="javascript:;" class="font-color font-color-default"></a></li>\n</ul>').appendTo(this.menuWrapper);
+    this.menuWrapper.on('mousedown', '.color-list', function(e) {
+      return false;
+    });
+    return this.menuWrapper.on('click', '.font-color', (function(_this) {
+      return function(e) {
+        var $link, $p, hex, range, rgb, textNode;
+        _this.wrapper.removeClass('menu-on');
+        $link = $(e.currentTarget);
+        if ($link.hasClass('font-color-default')) {
+          $p = _this.editor.body.find('p, li');
+          if (!($p.length > 0)) {
+            return;
+          }
+          rgb = window.getComputedStyle($p[0], null).getPropertyValue('color');
+          hex = _this._convertRgbToHex(rgb);
+        } else {
+          rgb = window.getComputedStyle($link[0], null).getPropertyValue('background-color');
+          hex = _this._convertRgbToHex(rgb);
+        }
+        if (!hex) {
+          return;
+        }
+        range = _this.editor.selection.range();
+        if (!$link.hasClass('font-color-default') && range.collapsed) {
+          textNode = document.createTextNode(_this._t('coloredText'));
+          range.insertNode(textNode);
+          range.selectNodeContents(textNode);
+        }
+        _this.editor.selection.range(range);
+        document.execCommand('styleWithCSS', false, true);
+        document.execCommand('foreColor', false, hex);
+        document.execCommand('styleWithCSS', false, false);
+        if (!_this.editor.util.support.oninput) {
+          return _this.editor.trigger('valuechanged');
+        }
+      };
+    })(this));
+  };
+
+  ColorButton.prototype._convertRgbToHex = function(rgb) {
+    var match, re, rgbToHex;
+    re = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/g;
+    match = re.exec(rgb);
+    if (!match) {
+      return '';
+    }
+    rgbToHex = function(r, g, b) {
+      var componentToHex;
+      componentToHex = function(c) {
+        var hex;
+        hex = c.toString(16);
+        if (hex.length === 1) {
+          return '0' + hex;
+        } else {
+          return hex;
+        }
+      };
+      return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
+    };
+    return rgbToHex(match[1] * 1, match[2] * 1, match[3] * 1);
+  };
+
+  return ColorButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(ColorButton);
+
+ListButton = (function(superClass) {
+  extend(ListButton, superClass);
+
+  function ListButton() {
+    return ListButton.__super__.constructor.apply(this, arguments);
+  }
+
+  ListButton.prototype.type = '';
+
+  ListButton.prototype.disableTag = 'pre, table';
+
+  ListButton.prototype.command = function(param) {
+    var $list, $rootNodes, anotherType;
+    $rootNodes = this.editor.selection.blockNodes();
+    anotherType = this.type === 'ul' ? 'ol' : 'ul';
+    this.editor.selection.save();
+    $list = null;
+    $rootNodes.each((function(_this) {
+      return function(i, node) {
+        var $node;
+        $node = $(node);
+        if ($node.is('blockquote, li') || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || !$.contains(document, node)) {
+          return;
+        }
+        if ($node.is(_this.type)) {
+          $node.children('li').each(function(i, li) {
+            var $childList, $li;
+            $li = $(li);
+            $childList = $li.children('ul, ol').insertAfter($node);
+            return $('<p/>').append($(li).html() || _this.editor.util.phBr).insertBefore($node);
+          });
+          return $node.remove();
+        } else if ($node.is(anotherType)) {
+          return $('<' + _this.type + '/>').append($node.contents()).replaceAll($node);
+        } else if ($list && $node.prev().is($list)) {
+          $('<li/>').append($node.html() || _this.editor.util.phBr).appendTo($list);
+          return $node.remove();
+        } else {
+          $list = $("<" + _this.type + "><li></li></" + _this.type + ">");
+          $list.find('li').append($node.html() || _this.editor.util.phBr);
+          return $list.replaceAll($node);
+        }
+      };
+    })(this));
+    this.editor.selection.restore();
+    return this.editor.trigger('valuechanged');
+  };
+
+  return ListButton;
+
+})(Button);
+
+OrderListButton = (function(superClass) {
+  extend(OrderListButton, superClass);
+
+  function OrderListButton() {
+    return OrderListButton.__super__.constructor.apply(this, arguments);
+  }
+
+  OrderListButton.prototype.type = 'ol';
+
+  OrderListButton.prototype.name = 'ol';
+
+  OrderListButton.prototype.icon = 'list-ol';
+
+  OrderListButton.prototype.htmlTag = 'ol';
+
+  OrderListButton.prototype.shortcut = 'cmd+/';
+
+  OrderListButton.prototype._init = function() {
+    if (this.editor.util.os.mac) {
+      this.title = this.title + ' ( Cmd + / )';
+    } else {
+      this.title = this.title + ' ( ctrl + / )';
+      this.shortcut = 'ctrl+/';
+    }
+    return OrderListButton.__super__._init.call(this);
+  };
+
+  return OrderListButton;
+
+})(ListButton);
+
+UnorderListButton = (function(superClass) {
+  extend(UnorderListButton, superClass);
+
+  function UnorderListButton() {
+    return UnorderListButton.__super__.constructor.apply(this, arguments);
+  }
+
+  UnorderListButton.prototype.type = 'ul';
+
+  UnorderListButton.prototype.name = 'ul';
+
+  UnorderListButton.prototype.icon = 'list-ul';
+
+  UnorderListButton.prototype.htmlTag = 'ul';
+
+  UnorderListButton.prototype.shortcut = 'cmd+.';
+
+  UnorderListButton.prototype._init = function() {
+    if (this.editor.util.os.mac) {
+      this.title = this.title + ' ( Cmd + . )';
+    } else {
+      this.title = this.title + ' ( Ctrl + . )';
+      this.shortcut = 'ctrl+.';
+    }
+    return UnorderListButton.__super__._init.call(this);
+  };
+
+  return UnorderListButton;
+
+})(ListButton);
+
+Simditor.Toolbar.addButton(OrderListButton);
+
+Simditor.Toolbar.addButton(UnorderListButton);
+
+BlockquoteButton = (function(superClass) {
+  extend(BlockquoteButton, superClass);
+
+  function BlockquoteButton() {
+    return BlockquoteButton.__super__.constructor.apply(this, arguments);
+  }
+
+  BlockquoteButton.prototype.name = 'blockquote';
+
+  BlockquoteButton.prototype.icon = 'quote-left';
+
+  BlockquoteButton.prototype.htmlTag = 'blockquote';
+
+  BlockquoteButton.prototype.disableTag = 'pre, table';
+
+  BlockquoteButton.prototype.command = function() {
+    var $rootNodes, clearCache, nodeCache;
+    $rootNodes = this.editor.selection.rootNodes();
+    $rootNodes = $rootNodes.filter(function(i, node) {
+      return !$(node).parent().is('blockquote');
+    });
+    this.editor.selection.save();
+    nodeCache = [];
+    clearCache = (function(_this) {
+      return function() {
+        if (nodeCache.length > 0) {
+          $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).append(nodeCache);
+          return nodeCache.length = 0;
+        }
+      };
+    })(this);
+    $rootNodes.each((function(_this) {
+      return function(i, node) {
+        var $node;
+        $node = $(node);
+        if (!$node.parent().is(_this.editor.body)) {
+          return;
+        }
+        if ($node.is(_this.htmlTag)) {
+          clearCache();
+          return $node.children().unwrap();
+        } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) {
+          return clearCache();
+        } else {
+          return nodeCache.push(node);
+        }
+      };
+    })(this));
+    clearCache();
+    this.editor.selection.restore();
+    return this.editor.trigger('valuechanged');
+  };
+
+  return BlockquoteButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(BlockquoteButton);
+
+CodeButton = (function(superClass) {
+  extend(CodeButton, superClass);
+
+  function CodeButton() {
+    return CodeButton.__super__.constructor.apply(this, arguments);
+  }
+
+  CodeButton.prototype.name = 'code';
+
+  CodeButton.prototype.icon = 'code';
+
+  CodeButton.prototype.htmlTag = 'pre';
+
+  CodeButton.prototype.disableTag = 'ul, ol, table';
+
+  CodeButton.prototype._init = function() {
+    CodeButton.__super__._init.call(this);
+    this.editor.on('decorate', (function(_this) {
+      return function(e, $el) {
+        return $el.find('pre').each(function(i, pre) {
+          return _this.decorate($(pre));
+        });
+      };
+    })(this));
+    return this.editor.on('undecorate', (function(_this) {
+      return function(e, $el) {
+        return $el.find('pre').each(function(i, pre) {
+          return _this.undecorate($(pre));
+        });
+      };
+    })(this));
+  };
+
+  CodeButton.prototype.render = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    CodeButton.__super__.render.apply(this, args);
+    return this.popover = new CodePopover({
+      button: this
+    });
+  };
+
+  CodeButton.prototype._checkMode = function() {
+    var $blockNodes, range;
+    range = this.editor.selection.range();
+    if (($blockNodes = $(range.cloneContents()).find(this.editor.util.blockNodes.join(','))) > 0 || (range.collapsed && this.editor.selection.startNodes().filter('code').length === 0)) {
+      this.inlineMode = false;
+      return this.htmlTag = 'pre';
+    } else {
+      this.inlineMode = true;
+      return this.htmlTag = 'code';
+    }
+  };
+
+  CodeButton.prototype._status = function() {
+    this._checkMode();
+    CodeButton.__super__._status.call(this);
+    if (this.inlineMode) {
+      return;
+    }
+    if (this.active) {
+      return this.popover.show(this.node);
+    } else {
+      return this.popover.hide();
+    }
+  };
+
+  CodeButton.prototype.decorate = function($pre) {
+    var $code, lang, ref, ref1;
+    $code = $pre.find('> code');
+    if ($code.length > 0) {
+      lang = (ref = $code.attr('class')) != null ? (ref1 = ref.match(/lang-(\S+)/)) != null ? ref1[1] : void 0 : void 0;
+      $code.contents().unwrap();
+      if (lang) {
+        return $pre.attr('data-lang', lang);
+      }
+    }
+  };
+
+  CodeButton.prototype.undecorate = function($pre) {
+    var $code, lang;
+    lang = $pre.attr('data-lang');
+    $code = $('<code/>');
+    if (lang && lang !== -1) {
+      $code.addClass('lang-' + lang);
+    }
+    return $pre.wrapInner($code).removeAttr('data-lang');
+  };
+
+  CodeButton.prototype.command = function() {
+    if (this.inlineMode) {
+      return this._inlineCommand();
+    } else {
+      return this._blockCommand();
+    }
+  };
+
+  CodeButton.prototype._blockCommand = function() {
+    var $rootNodes, clearCache, nodeCache, resultNodes;
+    $rootNodes = this.editor.selection.rootNodes();
+    nodeCache = [];
+    resultNodes = [];
+    clearCache = (function(_this) {
+      return function() {
+        var $pre;
+        if (!(nodeCache.length > 0)) {
+          return;
+        }
+        $pre = $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).text(_this.editor.formatter.clearHtml(nodeCache));
+        resultNodes.push($pre[0]);
+        return nodeCache.length = 0;
+      };
+    })(this);
+    $rootNodes.each((function(_this) {
+      return function(i, node) {
+        var $node, $p;
+        $node = $(node);
+        if ($node.is(_this.htmlTag)) {
+          clearCache();
+          $p = $('<p/>').append($node.html().replace('\n', '<br/>')).replaceAll($node);
+          return resultNodes.push($p[0]);
+        } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || $node.is('blockquote')) {
+          return clearCache();
+        } else {
+          return nodeCache.push(node);
+        }
+      };
+    })(this));
+    clearCache();
+    this.editor.selection.setRangeAtEndOf($(resultNodes).last());
+    return this.editor.trigger('valuechanged');
+  };
+
+  CodeButton.prototype._inlineCommand = function() {
+    var $code, $contents, range;
+    range = this.editor.selection.range();
+    if (this.active) {
+      range.selectNodeContents(this.node[0]);
+      this.editor.selection.save(range);
+      this.node.contents().unwrap();
+      this.editor.selection.restore();
+    } else {
+      $contents = $(range.extractContents());
+      $code = $("<" + this.htmlTag + "/>").append($contents.contents());
+      range.insertNode($code[0]);
+      range.selectNodeContents($code[0]);
+      this.editor.selection.range(range);
+    }
+    return this.editor.trigger('valuechanged');
+  };
+
+  return CodeButton;
+
+})(Button);
+
+CodePopover = (function(superClass) {
+  extend(CodePopover, superClass);
+
+  function CodePopover() {
+    return CodePopover.__super__.constructor.apply(this, arguments);
+  }
+
+  CodePopover.prototype.render = function() {
+    var $option, k, lang, len, ref;
+    this._tpl = "<div class=\"code-settings\">\n  <div class=\"settings-field\">\n    <select class=\"select-lang\">\n      <option value=\"-1\">" + (this._t('selectLanguage')) + "</option>\n    </select>\n  </div>\n</div>";
+    this.langs = this.editor.opts.codeLanguages || [
+      {
+        name: 'Bash',
+        value: 'bash'
+      }, {
+        name: 'C++',
+        value: 'c++'
+      }, {
+        name: 'C#',
+        value: 'cs'
+      }, {
+        name: 'CSS',
+        value: 'css'
+      }, {
+        name: 'Erlang',
+        value: 'erlang'
+      }, {
+        name: 'Less',
+        value: 'less'
+      }, {
+        name: 'Sass',
+        value: 'sass'
+      }, {
+        name: 'Diff',
+        value: 'diff'
+      }, {
+        name: 'CoffeeScript',
+        value: 'coffeescript'
+      }, {
+        name: 'HTML,XML',
+        value: 'html'
+      }, {
+        name: 'JSON',
+        value: 'json'
+      }, {
+        name: 'Java',
+        value: 'java'
+      }, {
+        name: 'JavaScript',
+        value: 'js'
+      }, {
+        name: 'Markdown',
+        value: 'markdown'
+      }, {
+        name: 'Objective C',
+        value: 'oc'
+      }, {
+        name: 'PHP',
+        value: 'php'
+      }, {
+        name: 'Perl',
+        value: 'parl'
+      }, {
+        name: 'Python',
+        value: 'python'
+      }, {
+        name: 'Ruby',
+        value: 'ruby'
+      }, {
+        name: 'SQL',
+        value: 'sql'
+      }
+    ];
+    this.el.addClass('code-popover').append(this._tpl);
+    this.selectEl = this.el.find('.select-lang');
+    ref = this.langs;
+    for (k = 0, len = ref.length; k < len; k++) {
+      lang = ref[k];
+      $option = $('<option/>', {
+        text: lang.name,
+        value: lang.value
+      }).appendTo(this.selectEl);
+    }
+    this.selectEl.on('change', (function(_this) {
+      return function(e) {
+        var selected;
+        _this.lang = _this.selectEl.val();
+        selected = _this.target.hasClass('selected');
+        _this.target.removeClass().removeAttr('data-lang');
+        if (_this.lang !== -1) {
+          _this.target.attr('data-lang', _this.lang);
+        }
+        if (selected) {
+          _this.target.addClass('selected');
+        }
+        return _this.editor.trigger('valuechanged');
+      };
+    })(this));
+    return this.editor.on('valuechanged', (function(_this) {
+      return function(e) {
+        if (_this.active) {
+          return _this.refresh();
+        }
+      };
+    })(this));
+  };
+
+  CodePopover.prototype.show = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    CodePopover.__super__.show.apply(this, args);
+    this.lang = this.target.attr('data-lang');
+    if (this.lang != null) {
+      return this.selectEl.val(this.lang);
+    } else {
+      return this.selectEl.val(-1);
+    }
+  };
+
+  return CodePopover;
+
+})(Popover);
+
+Simditor.Toolbar.addButton(CodeButton);
+
+LinkButton = (function(superClass) {
+  extend(LinkButton, superClass);
+
+  function LinkButton() {
+    return LinkButton.__super__.constructor.apply(this, arguments);
+  }
+
+  LinkButton.prototype.name = 'link';
+
+  LinkButton.prototype.icon = 'link';
+
+  LinkButton.prototype.htmlTag = 'a';
+
+  LinkButton.prototype.disableTag = 'pre';
+
+  LinkButton.prototype.render = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    LinkButton.__super__.render.apply(this, args);
+    return this.popover = new LinkPopover({
+      button: this
+    });
+  };
+
+  LinkButton.prototype._status = function() {
+    LinkButton.__super__._status.call(this);
+    if (this.active && !this.editor.selection.rangeAtEndOf(this.node)) {
+      return this.popover.show(this.node);
+    } else {
+      return this.popover.hide();
+    }
+  };
+
+  LinkButton.prototype.command = function() {
+    var $contents, $link, $newBlock, linkText, range, txtNode;
+    range = this.editor.selection.range();
+    if (this.active) {
+      txtNode = document.createTextNode(this.node.text());
+      this.node.replaceWith(txtNode);
+      range.selectNode(txtNode);
+    } else {
+      $contents = $(range.extractContents());
+      linkText = this.editor.formatter.clearHtml($contents.contents(), false);
+      $link = $('<a/>', {
+        href: '',
+        target: '_blank',
+        text: linkText || this._t('linkText')
+      });
+      if (this.editor.selection.blockNodes().length > 0) {
+        range.insertNode($link[0]);
+      } else {
+        $newBlock = $('<p/>').append($link);
+        range.insertNode($newBlock[0]);
+      }
+      range.selectNodeContents($link[0]);
+      this.popover.one('popovershow', (function(_this) {
+        return function() {
+          if (linkText) {
+            _this.popover.urlEl.focus();
+            return _this.popover.urlEl[0].select();
+          } else {
+            _this.popover.textEl.focus();
+            return _this.popover.textEl[0].select();
+          }
+        };
+      })(this));
+    }
+    this.editor.selection.range(range);
+    return this.editor.trigger('valuechanged');
+  };
+
+  return LinkButton;
+
+})(Button);
+
+LinkPopover = (function(superClass) {
+  extend(LinkPopover, superClass);
+
+  function LinkPopover() {
+    return LinkPopover.__super__.constructor.apply(this, arguments);
+  }
+
+  LinkPopover.prototype.render = function() {
+    var tpl;
+    tpl = "<div class=\"link-settings\">\n  <div class=\"settings-field\">\n    <label>" + (this._t('linkText')) + "</label>\n    <input class=\"link-text\" type=\"text\"/>\n    <a class=\"btn-unlink\" href=\"javascript:;\" title=\"" + (this._t('removeLink')) + "\"\n      tabindex=\"-1\">\n      <span class=\"simditor-icon simditor-icon-unlink\"></span>\n    </a>\n  </div>\n  <div class=\"settings-field\">\n    <label>" + (this._t('linkUrl')) + "</label>\n    <input class=\"link-url\" type=\"text\"/>\n  </div>\n  <div class=\"settings-field\">\n    <label>" + (this._t('linkTarget')) + "</label>\n    <select class=\"link-target\">\n      <option value=\"_blank\">" + (this._t('openLinkInNewWindow')) + " (_blank)</option>\n      <option value=\"_self\">" + (this._t('openLinkInCurrentWindow')) + " (_self)</option>\n    </select>\n  </div>\n</div>";
+    this.el.addClass('link-popover').append(tpl);
+    this.textEl = this.el.find('.link-text');
+    this.urlEl = this.el.find('.link-url');
+    this.unlinkEl = this.el.find('.btn-unlink');
+    this.selectTarget = this.el.find('.link-target');
+    this.textEl.on('keyup', (function(_this) {
+      return function(e) {
+        if (e.which === 13) {
+          return;
+        }
+        _this.target.text(_this.textEl.val());
+        return _this.editor.inputManager.throttledValueChanged();
+      };
+    })(this));
+    this.urlEl.on('keyup', (function(_this) {
+      return function(e) {
+        var val;
+        if (e.which === 13) {
+          return;
+        }
+        val = _this.urlEl.val();
+        if (!(/https?:\/\/|^\//ig.test(val) || !val)) {
+          val = 'http://' + val;
+        }
+        _this.target.attr('href', val);
+        return _this.editor.inputManager.throttledValueChanged();
+      };
+    })(this));
+    $([this.urlEl[0], this.textEl[0]]).on('keydown', (function(_this) {
+      return function(e) {
+        var range;
+        if (e.which === 13 || e.which === 27 || (!e.shiftKey && e.which === 9 && $(e.target).hasClass('link-url'))) {
+          e.preventDefault();
+          range = document.createRange();
+          _this.editor.selection.setRangeAfter(_this.target, range);
+          _this.hide();
+          return _this.editor.inputManager.throttledValueChanged();
+        }
+      };
+    })(this));
+    this.unlinkEl.on('click', (function(_this) {
+      return function(e) {
+        var range, txtNode;
+        txtNode = document.createTextNode(_this.target.text());
+        _this.target.replaceWith(txtNode);
+        _this.hide();
+        range = document.createRange();
+        _this.editor.selection.setRangeAfter(txtNode, range);
+        return _this.editor.inputManager.throttledValueChanged();
+      };
+    })(this));
+    return this.selectTarget.on('change', (function(_this) {
+      return function(e) {
+        _this.target.attr('target', _this.selectTarget.val());
+        return _this.editor.inputManager.throttledValueChanged();
+      };
+    })(this));
+  };
+
+  LinkPopover.prototype.show = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    LinkPopover.__super__.show.apply(this, args);
+    this.textEl.val(this.target.text());
+    return this.urlEl.val(this.target.attr('href'));
+  };
+
+  return LinkPopover;
+
+})(Popover);
+
+Simditor.Toolbar.addButton(LinkButton);
+
+ImageButton = (function(superClass) {
+  extend(ImageButton, superClass);
+
+  function ImageButton() {
+    return ImageButton.__super__.constructor.apply(this, arguments);
+  }
+
+  ImageButton.prototype.name = 'image';
+
+  ImageButton.prototype.icon = 'picture-o';
+
+  ImageButton.prototype.htmlTag = 'img';
+
+  ImageButton.prototype.disableTag = 'pre, table';
+
+  ImageButton.prototype.defaultImage = '';
+
+  ImageButton.prototype.needFocus = false;
+
+  ImageButton.prototype._init = function() {
+    var item, k, len, ref;
+    if (this.editor.opts.imageButton) {
+      if (Array.isArray(this.editor.opts.imageButton)) {
+        this.menu = [];
+        ref = this.editor.opts.imageButton;
+        for (k = 0, len = ref.length; k < len; k++) {
+          item = ref[k];
+          this.menu.push({
+            name: item + '-image',
+            text: this._t(item + 'Image')
+          });
+        }
+      } else {
+        this.menu = false;
+      }
+    } else {
+      if (this.editor.uploader != null) {
+        this.menu = [
+          {
+            name: 'upload-image',
+            text: this._t('uploadImage')
+          }, {
+            name: 'external-image',
+            text: this._t('externalImage')
+          }, {
+              name: 'select-image',
+              text: this._t('selectImage')
+          }
+        ];
+      } else {
+        this.menu = false;
+      }
+    }
+    this.defaultImage = this.editor.opts.defaultImage;
+    this.editor.body.on('click', 'img:not([data-non-image])', (function(_this) {
+      return function(e) {
+        var $img, range;
+        $img = $(e.currentTarget);
+        range = document.createRange();
+        range.selectNode($img[0]);
+        _this.editor.selection.range(range);
+        if (!_this.editor.util.support.onselectionchange) {
+          _this.editor.trigger('selectionchanged');
+        }
+        return false;
+      };
+    })(this));
+    this.editor.body.on('mouseup', 'img:not([data-non-image])', function(e) {
+      return false;
+    });
+    this.editor.on('selectionchanged.image', (function(_this) {
+      return function() {
+        var $contents, $img, range;
+        range = _this.editor.selection.range();
+        if (range == null) {
+          return;
+        }
+        $contents = $(range.cloneContents()).contents();
+        if ($contents.length === 1 && $contents.is('img:not([data-non-image])')) {
+          $img = $(range.startContainer).contents().eq(range.startOffset);
+          return _this.popover.show($img);
+        } else {
+          return _this.popover.hide();
+        }
+      };
+    })(this));
+    this.editor.on('valuechanged.image', (function(_this) {
+      return function() {
+        var $masks;
+        $masks = _this.editor.wrapper.find('.simditor-image-loading');
+        if (!($masks.length > 0)) {
+          return;
+        }
+        return $masks.each(function(i, mask) {
+          var $img, $mask, file;
+          $mask = $(mask);
+          $img = $mask.data('img');
+          if (!($img && $img.parent().length > 0)) {
+            $mask.remove();
+            if ($img) {
+              file = $img.data('file');
+              if (file) {
+                _this.editor.uploader.cancel(file);
+                if (_this.editor.body.find('img.uploading').length < 1) {
+                  return _this.editor.uploader.trigger('uploadready', [file]);
+                }
+              }
+            }
+          }
+        });
+      };
+    })(this));
+    return ImageButton.__super__._init.call(this);
+  };
+
+  ImageButton.prototype.render = function() {
+    var args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    ImageButton.__super__.render.apply(this, args);
+    this.popover = new ImagePopover({
+      button: this
+    });
+    if (this.editor.opts.imageButton === 'upload') {
+      return this._initUploader(this.el);
+    }
+  };
+
+  ImageButton.prototype.renderMenu = function() {
+    ImageButton.__super__.renderMenu.call(this);
+    return this._initUploader();
+  };
+
+  ImageButton.prototype._initUploader = function($uploadItem) {
+    var $input, createInput, uploadProgress;
+    if ($uploadItem == null) {
+      $uploadItem = this.menuEl.find('.menu-item-upload-image');
+    }
+    if (this.editor.uploader == null) {
+      this.el.find('.btn-upload').remove();
+      return;
+    }
+    $input = null;
+    createInput = (function(_this) {
+      return function() {
+        if ($input) {
+          $input.remove();
+        }
+        return $input = $('<input/>', {
+          type: 'file',
+          title: _this._t('uploadImage'),
+          multiple: true,
+          accept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg'
+        }).appendTo($uploadItem);
+      };
+    })(this);
+    createInput();
+    $uploadItem.on('click mousedown', 'input[type=file]', function(e) {
+      return e.stopPropagation();
+    });
+    $uploadItem.on('change', 'input[type=file]', (function(_this) {
+      return function(e) {
+        if (_this.editor.inputManager.focused) {
+          _this.editor.uploader.upload($input, {
+            inline: true
+          });
+          createInput();
+        } else {
+          _this.editor.one('focus', function(e) {
+            _this.editor.uploader.upload($input, {
+              inline: true
+            });
+            return createInput();
+          });
+          _this.editor.focus();
+        }
+        return _this.wrapper.removeClass('menu-on');
+      };
+    })(this));
+    this.editor.uploader.on('beforeupload', (function(_this) {
+      return function(e, file) {
+        var $img;
+        if (!file.inline) {
+          return;
+        }
+        if (file.img) {
+          $img = $(file.img);
+        } else {
+          $img = _this.createImage(file.name);
+          file.img = $img;
+        }
+        $img.addClass('uploading');
+        $img.data('file', file);
+        return _this.editor.uploader.readImageFile(file.obj, function(img) {
+          var src;
+          if (!$img.hasClass('uploading')) {
+            return;
+          }
+          src = img ? img.src : _this.defaultImage;
+          return _this.loadImage($img, src, function() {
+            if (_this.popover.active) {
+              _this.popover.refresh();
+              return _this.popover.srcEl.val(_this._t('uploading')).prop('disabled', true);
+            }
+          });
+        });
+      };
+    })(this));
+    uploadProgress = $.proxy(this.editor.util.throttle(function(e, file, loaded, total) {
+      var $img, $mask, percent;
+      if (!file.inline) {
+        return;
+      }
+      $mask = file.img.data('mask');
+      if (!$mask) {
+        return;
+      }
+      $img = $mask.data('img');
+      if (!($img.hasClass('uploading') && $img.parent().length > 0)) {
+        $mask.remove();
+        return;
+      }
+      percent = loaded / total;
+      percent = (percent * 100).toFixed(0);
+      if (percent > 99) {
+        percent = 99;
+      }
+      return $mask.find('.progress').height((100 - percent) + "%");
+    }, 500), this);
+    this.editor.uploader.on('uploadprogress', uploadProgress);
+    this.editor.uploader.on('uploadsuccess', (function(_this) {
+      return function(e, file, result) {
+        var $img, img_path, msg;
+        if (!file.inline) {
+          return;
+        }
+        $img = file.img;
+        if (!($img.hasClass('uploading') && $img.parent().length > 0)) {
+          return;
+        }
+        if (typeof result !== 'object') {
+          try {
+            result = $.parseJSON(result);
+          } catch (_error) {
+            e = _error;
+            result = {
+              success: false
+            };
+          }
+        }
+        if (result.success === false) {
+          msg = result.msg || _this._t('uploadFailed');
+          alert(msg);
+          img_path = _this.defaultImage;
+        } else {
+          img_path = result.file_path;
+        }
+        _this.loadImage($img, img_path, function() {
+          var $mask;
+          $img.removeData('file');
+          $img.removeClass('uploading').removeClass('loading');
+          $mask = $img.data('mask');
+          if ($mask) {
+            $mask.remove();
+          }
+          $img.removeData('mask');
+          _this.editor.trigger('valuechanged');
+          if (_this.editor.body.find('img.uploading').length < 1) {
+            return _this.editor.uploader.trigger('uploadready', [file, result]);
+          }
+        });
+        if (_this.popover.active) {
+          _this.popover.srcEl.prop('disabled', false);
+          return _this.popover.srcEl.val(result.file_path);
+        }
+      };
+    })(this));
+    return this.editor.uploader.on('uploaderror', (function(_this) {
+      return function(e, file, xhr) {
+        var $img, msg, result;
+        if (!file.inline) {
+          return;
+        }
+        if (xhr.statusText === 'abort') {
+          return;
+        }
+        if (xhr.responseText) {
+          try {
+            result = $.parseJSON(xhr.responseText);
+            msg = result.msg;
+          } catch (_error) {
+            e = _error;
+            msg = _this._t('uploadError');
+          }
+        }
+        $img = file.img;
+        if (!($img.hasClass('uploading') && $img.parent().length > 0)) {
+          return;
+        }
+        _this.loadImage($img, _this.defaultImage, function() {
+          var $mask;
+          $img.removeData('file');
+          $img.removeClass('uploading').removeClass('loading');
+          $mask = $img.data('mask');
+          if ($mask) {
+            $mask.remove();
+          }
+          return $img.removeData('mask');
+        });
+        if (_this.popover.active) {
+          _this.popover.srcEl.prop('disabled', false);
+          _this.popover.srcEl.val(_this.defaultImage);
+        }
+        _this.editor.trigger('valuechanged');
+        if (_this.editor.body.find('img.uploading').length < 1) {
+          return _this.editor.uploader.trigger('uploadready', [file, result]);
+        }
+      };
+    })(this));
+  };
+
+  ImageButton.prototype._status = function() {
+    return this._disableStatus();
+  };
+
+  ImageButton.prototype.loadImage = function($img, src, callback) {
+    var $mask, img, positionMask;
+    positionMask = (function(_this) {
+      return function() {
+        var imgOffset, wrapperOffset;
+        imgOffset = $img.offset();
+        wrapperOffset = _this.editor.wrapper.offset();
+        return $mask.css({
+          top: imgOffset.top - wrapperOffset.top,
+          left: imgOffset.left - wrapperOffset.left,
+          width: $img.width(),
+          height: $img.height()
+        }).show();
+      };
+    })(this);
+    $img.addClass('loading');
+    $mask = $img.data('mask');
+    if (!$mask) {
+      $mask = $('<div class="simditor-image-loading">\n  <div class="progress"></div>\n</div>').hide().appendTo(this.editor.wrapper);
+      positionMask();
+      $img.data('mask', $mask);
+      $mask.data('img', $img);
+    }
+    img = new Image();
+    img.onload = (function(_this) {
+      return function() {
+        var height, width;
+        if (!$img.hasClass('loading') && !$img.hasClass('uploading')) {
+          return;
+        }
+        width = img.width;
+        height = img.height;
+        $img.attr({
+          src: src,
+          width: width,
+          height: height,
+          'data-image-size': width + ',' + height
+        }).removeClass('loading');
+        if ($img.hasClass('uploading')) {
+          _this.editor.util.reflow(_this.editor.body);
+          positionMask();
+        } else {
+          $mask.remove();
+          $img.removeData('mask');
+        }
+        if ($.isFunction(callback)) {
+          return callback(img);
+        }
+      };
+    })(this);
+    img.onerror = function() {
+      if ($.isFunction(callback)) {
+        callback(false);
+      }
+      $mask.remove();
+      return $img.removeData('mask').removeClass('loading');
+    };
+    return img.src = src;
+  };
+
+  ImageButton.prototype.createImage = function(name) {
+    var $img, range;
+    if (name == null) {
+      name = 'Image';
+    }
+    if (!this.editor.inputManager.focused) {
+      this.editor.focus();
+    }
+    range = this.editor.selection.range();
+    range.deleteContents();
+    this.editor.selection.range(range);
+    $img = $('<img/>').attr('alt', name);
+    range.insertNode($img[0]);
+    this.editor.selection.setRangeAfter($img, range);
+    this.editor.trigger('valuechanged');
+    return $img;
+  };
+
+  ImageButton.prototype.command = function(src) {
+    var $img;
+
+    $img = this.createImage();
+    return this.loadImage($img, src || this.defaultImage, (function(_this) {
+      return function() {
+        _this.editor.trigger('valuechanged');
+        _this.editor.util.reflow($img);
+        $img.click();
+        return _this.popover.one('popovershow', function() {
+          _this.popover.srcEl.focus();
+          return _this.popover.srcEl[0].select();
+        });
+      };
+    })(this));
+  };
+
+  return ImageButton;
+
+})(Button);
+
+ImagePopover = (function(superClass) {
+  extend(ImagePopover, superClass);
+
+  function ImagePopover() {
+    return ImagePopover.__super__.constructor.apply(this, arguments);
+  }
+
+  ImagePopover.prototype.offset = {
+    top: 6,
+    left: -4
+  };
+
+  ImagePopover.prototype.render = function() {
+    var tpl;
+    tpl = "<div class=\"link-settings\">\n  <div class=\"settings-field\">\n    <label>" + (this._t('imageUrl')) + "</label>\n    <input class=\"image-src\" type=\"text\" tabindex=\"1\" />\n    <a class=\"btn-upload\" href=\"javascript:;\"\n      title=\"" + (this._t('uploadImage')) + "\" tabindex=\"-1\">\n      <span class=\"simditor-icon simditor-icon-upload\"></span>\n    </a>\n  </div>\n  <div class='settings-field'>\n    <label>" + (this._t('imageAlt')) + "</label>\n    <input class=\"image-alt\" id=\"image-alt\" type=\"text\" tabindex=\"1\" />\n  </div>\n  <div class=\"settings-field\">\n    <label>" + (this._t('imageSize')) + "</label>\n    <input class=\"image-size\" id=\"image-width\" type=\"text\" tabindex=\"2\" />\n    <span class=\"times\">×</span>\n    <input class=\"image-size\" id=\"image-height\" type=\"text\" tabindex=\"3\" />\n    <a class=\"btn-restore\" href=\"javascript:;\"\n      title=\"" + (this._t('restoreImageSize')) + "\" tabindex=\"-1\">\n      <span class=\"simditor-icon simditor-icon-undo\"></span>\n    </a>\n  </div>\n</div>";
+    this.el.addClass('image-popover').append(tpl);
+    this.srcEl = this.el.find('.image-src');
+    this.widthEl = this.el.find('#image-width');
+    this.heightEl = this.el.find('#image-height');
+    this.altEl = this.el.find('#image-alt');
+    this.srcEl.on('keydown', (function(_this) {
+      return function(e) {
+        var range;
+        if (!(e.which === 13 && !_this.target.hasClass('uploading'))) {
+          return;
+        }
+        e.preventDefault();
+        range = document.createRange();
+        _this.button.editor.selection.setRangeAfter(_this.target, range);
+        return _this.hide();
+      };
+    })(this));
+    this.srcEl.on('blur', (function(_this) {
+      return function(e) {
+        return _this._loadImage(_this.srcEl.val());
+      };
+    })(this));
+    this.el.find('.image-size').on('blur', (function(_this) {
+      return function(e) {
+        _this._resizeImg($(e.currentTarget));
+        return _this.el.data('popover').refresh();
+      };
+    })(this));
+    this.el.find('.image-size').on('keyup', (function(_this) {
+      return function(e) {
+        var inputEl;
+        inputEl = $(e.currentTarget);
+        if (!(e.which === 13 || e.which === 27 || e.which === 9)) {
+          return _this._resizeImg(inputEl, true);
+        }
+      };
+    })(this));
+    this.el.find('.image-size').on('keydown', (function(_this) {
+      return function(e) {
+        var $img, inputEl, range;
+        inputEl = $(e.currentTarget);
+        if (e.which === 13 || e.which === 27) {
+          e.preventDefault();
+          if (e.which === 13) {
+            _this._resizeImg(inputEl);
+          } else {
+            _this._restoreImg();
+          }
+          $img = _this.target;
+          _this.hide();
+          range = document.createRange();
+          return _this.button.editor.selection.setRangeAfter($img, range);
+        } else if (e.which === 9) {
+          return _this.el.data('popover').refresh();
+        }
+      };
+    })(this));
+    this.altEl.on('keydown', (function(_this) {
+      return function(e) {
+        var range;
+        if (e.which === 13) {
+          e.preventDefault();
+          range = document.createRange();
+          _this.button.editor.selection.setRangeAfter(_this.target, range);
+          return _this.hide();
+        }
+      };
+    })(this));
+    this.altEl.on('keyup', (function(_this) {
+      return function(e) {
+        if (e.which === 13 || e.which === 27 || e.which === 9) {
+          return;
+        }
+        _this.alt = _this.altEl.val();
+        return _this.target.attr('alt', _this.alt);
+      };
+    })(this));
+    this.el.find('.btn-restore').on('click', (function(_this) {
+      return function(e) {
+        _this._restoreImg();
+        return _this.el.data('popover').refresh();
+      };
+    })(this));
+    this.editor.on('valuechanged', (function(_this) {
+      return function(e) {
+        if (_this.active) {
+          return _this.refresh();
+        }
+      };
+    })(this));
+    return this._initUploader();
+  };
+
+  ImagePopover.prototype._initUploader = function() {
+    var $uploadBtn, createInput;
+    $uploadBtn = this.el.find('.btn-upload');
+    if (this.editor.uploader == null) {
+      $uploadBtn.remove();
+      return;
+    }
+    createInput = (function(_this) {
+      return function() {
+        if (_this.input) {
+          _this.input.remove();
+        }
+        return _this.input = $('<input/>', {
+          type: 'file',
+          title: _this._t('uploadImage'),
+          multiple: true,
+          accept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg'
+        }).appendTo($uploadBtn);
+      };
+    })(this);
+    createInput();
+    this.el.on('click mousedown', 'input[type=file]', function(e) {
+      return e.stopPropagation();
+    });
+    return this.el.on('change', 'input[type=file]', (function(_this) {
+      return function(e) {
+        _this.editor.uploader.upload(_this.input, {
+          inline: true,
+          img: _this.target
+        });
+        return createInput();
+      };
+    })(this));
+  };
+
+  ImagePopover.prototype._resizeImg = function(inputEl, onlySetVal) {
+    var height, value, width;
+    if (onlySetVal == null) {
+      onlySetVal = false;
+    }
+    value = inputEl.val() * 1;
+    if (!(this.target && ($.isNumeric(value) || value < 0))) {
+      return;
+    }
+    if (inputEl.is(this.widthEl)) {
+      width = value;
+      height = this.height * value / this.width;
+      this.heightEl.val(height);
+    } else {
+      height = value;
+      width = this.width * value / this.height;
+      this.widthEl.val(width);
+    }
+    if (!onlySetVal) {
+      this.target.attr({
+        width: width,
+        height: height
+      });
+      return this.editor.trigger('valuechanged');
+    }
+  };
+
+  ImagePopover.prototype._restoreImg = function() {
+    var ref, size;
+    size = ((ref = this.target.data('image-size')) != null ? ref.split(",") : void 0) || [this.width, this.height];
+    this.target.attr({
+      width: size[0] * 1,
+      height: size[1] * 1
+    });
+    this.widthEl.val(size[0]);
+    this.heightEl.val(size[1]);
+    return this.editor.trigger('valuechanged');
+  };
+
+  ImagePopover.prototype._loadImage = function(src, callback) {
+    if (/^data:image/.test(src) && !this.editor.uploader) {
+      if (callback) {
+        callback(false);
+      }
+      return;
+    }
+    if (this.target.attr('src') === src) {
+      return;
+    }
+    return this.button.loadImage(this.target, src, (function(_this) {
+
+      return function(img) {
+        var blob;
+        if (!img) {
+          return;
+        }
+        if (_this.active) {
+          _this.width = img.width;
+          _this.height = img.height;
+          _this.widthEl.val(_this.width);
+          _this.heightEl.val(_this.height);
+        }
+        if (/^data:image/.test(src)) {
+          blob = _this.editor.util.dataURLtoBlob(src);
+          blob.name = "Base64 Image.png";
+          _this.editor.uploader.upload(blob, {
+            inline: true,
+            img: _this.target
+          });
+        } else {
+          _this.editor.trigger('valuechanged');
+        }
+        if (callback) {
+          return callback(img);
+        }
+      };
+    })(this));
+  };
+
+  ImagePopover.prototype.show = function() {
+    var $img, args;
+    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
+    ImagePopover.__super__.show.apply(this, args);
+    $img = this.target;
+    this.width = $img.width();
+    this.height = $img.height();
+    this.alt = $img.attr('alt');
+    if ($img.hasClass('uploading')) {
+      return this.srcEl.val(this._t('uploading')).prop('disabled', true);
+    } else {
+      this.srcEl.val($img.attr('src')).prop('disabled', false);
+      this.widthEl.val(this.width);
+      this.heightEl.val(this.height);
+      return this.altEl.val(this.alt);
+    }
+  };
+
+  return ImagePopover;
+
+})(Popover);
+
+Simditor.Toolbar.addButton(ImageButton);
+
+IndentButton = (function(superClass) {
+  extend(IndentButton, superClass);
+
+  function IndentButton() {
+    return IndentButton.__super__.constructor.apply(this, arguments);
+  }
+
+  IndentButton.prototype.name = 'indent';
+
+  IndentButton.prototype.icon = 'indent';
+
+  IndentButton.prototype._init = function() {
+    var hotkey;
+    hotkey = this.editor.opts.tabIndent === false ? '' : ' (Tab)';
+    this.title = this._t(this.name) + hotkey;
+    return IndentButton.__super__._init.call(this);
+  };
+
+  IndentButton.prototype._status = function() {};
+
+  IndentButton.prototype.command = function() {
+    return this.editor.indentation.indent();
+  };
+
+  return IndentButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(IndentButton);
+
+OutdentButton = (function(superClass) {
+  extend(OutdentButton, superClass);
+
+  function OutdentButton() {
+    return OutdentButton.__super__.constructor.apply(this, arguments);
+  }
+
+  OutdentButton.prototype.name = 'outdent';
+
+  OutdentButton.prototype.icon = 'outdent';
+
+  OutdentButton.prototype._init = function() {
+    var hotkey;
+    hotkey = this.editor.opts.tabIndent === false ? '' : ' (Shift + Tab)';
+    this.title = this._t(this.name) + hotkey;
+    return OutdentButton.__super__._init.call(this);
+  };
+
+  OutdentButton.prototype._status = function() {};
+
+  OutdentButton.prototype.command = function() {
+    return this.editor.indentation.indent(true);
+  };
+
+  return OutdentButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(OutdentButton);
+
+HrButton = (function(superClass) {
+  extend(HrButton, superClass);
+
+  function HrButton() {
+    return HrButton.__super__.constructor.apply(this, arguments);
+  }
+
+  HrButton.prototype.name = 'hr';
+
+  HrButton.prototype.icon = 'minus';
+
+  HrButton.prototype.htmlTag = 'hr';
+
+  HrButton.prototype._status = function() {};
+
+  HrButton.prototype.command = function() {
+    var $hr, $newBlock, $nextBlock, $rootBlock;
+    $rootBlock = this.editor.selection.rootNodes().first();
+    $nextBlock = $rootBlock.next();
+    if ($nextBlock.length > 0) {
+      this.editor.selection.save();
+    } else {
+      $newBlock = $('<p/>').append(this.editor.util.phBr);
+    }
+    $hr = $('<hr/>').insertAfter($rootBlock);
+    if ($newBlock) {
+      $newBlock.insertAfter($hr);
+      this.editor.selection.setRangeAtStartOf($newBlock);
+    } else {
+      this.editor.selection.restore();
+    }
+    return this.editor.trigger('valuechanged');
+  };
+
+  return HrButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(HrButton);
+
+TableButton = (function(superClass) {
+  extend(TableButton, superClass);
+
+  function TableButton() {
+    return TableButton.__super__.constructor.apply(this, arguments);
+  }
+
+  TableButton.prototype.name = 'table';
+
+  TableButton.prototype.icon = 'table';
+
+  TableButton.prototype.htmlTag = 'table';
+
+  TableButton.prototype.disableTag = 'pre, li, blockquote';
+
+  TableButton.prototype.menu = true;
+
+  TableButton.prototype._init = function() {
+    TableButton.__super__._init.call(this);
+    $.merge(this.editor.formatter._allowedTags, ['thead', 'th', 'tbody', 'tr', 'td', 'colgroup', 'col']);
+    $.extend(this.editor.formatter._allowedAttributes, {
+      td: ['rowspan', 'colspan'],
+      col: ['width']
+    });
+    $.extend(this.editor.formatter._allowedStyles, {
+      td: ['text-align'],
+      th: ['text-align']
+    });
+    this._initShortcuts();
+    this.editor.on('decorate', (function(_this) {
+      return function(e, $el) {
+        return $el.find('table').each(function(i, table) {
+          return _this.decorate($(table));
+        });
+      };
+    })(this));
+    this.editor.on('undecorate', (function(_this) {
+      return function(e, $el) {
+        return $el.find('table').each(function(i, table) {
+          return _this.undecorate($(table));
+        });
+      };
+    })(this));
+    this.editor.on('selectionchanged.table', (function(_this) {
+      return function(e) {
+        var $container, range;
+        _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active');
+        range = _this.editor.selection.range();
+        if (!range) {
+          return;
+        }
+        $container = _this.editor.selection.containerNode();
+        if (range.collapsed && $container.is('.simditor-table')) {
+          if (_this.editor.selection.rangeAtStartOf($container)) {
+            $container = $container.find('th:first');
+          } else {
+            $container = $container.find('td:last');
+          }
+          _this.editor.selection.setRangeAtEndOf($container);
+        }
+        return $container.closest('td, th', _this.editor.body).addClass('active');
+      };
+    })(this));
+    this.editor.on('blur.table', (function(_this) {
+      return function(e) {
+        return _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active');
+      };
+    })(this));
+    this.editor.keystroke.add('up', 'td', (function(_this) {
+      return function(e, $node) {
+        _this._tdNav($node, 'up');
+        return true;
+      };
+    })(this));
+    this.editor.keystroke.add('up', 'th', (function(_this) {
+      return function(e, $node) {
+        _this._tdNav($node, 'up');
+        return true;
+      };
+    })(this));
+    this.editor.keystroke.add('down', 'td', (function(_this) {
+      return function(e, $node) {
+        _this._tdNav($node, 'down');
+        return true;
+      };
+    })(this));
+    return this.editor.keystroke.add('down', 'th', (function(_this) {
+      return function(e, $node) {
+        _this._tdNav($node, 'down');
+        return true;
+      };
+    })(this));
+  };
+
+  TableButton.prototype._tdNav = function($td, direction) {
+    var $anotherTr, $tr, action, anotherTag, index, parentTag, ref;
+    if (direction == null) {
+      direction = 'up';
+    }
+    action = direction === 'up' ? 'prev' : 'next';
+    ref = direction === 'up' ? ['tbody', 'thead'] : ['thead', 'tbody'], parentTag = ref[0], anotherTag = ref[1];
+    $tr = $td.parent('tr');
+    $anotherTr = this["_" + action + "Row"]($tr);
+    if (!($anotherTr.length > 0)) {
+      return true;
+    }
+    index = $tr.find('td, th').index($td);
+    return this.editor.selection.setRangeAtEndOf($anotherTr.find('td, th').eq(index));
+  };
+
+  TableButton.prototype._nextRow = function($tr) {
+    var $nextTr;
+    $nextTr = $tr.next('tr');
+    if ($nextTr.length < 1 && $tr.parent('thead').length > 0) {
+      $nextTr = $tr.parent('thead').next('tbody').find('tr:first');
+    }
+    return $nextTr;
+  };
+
+  TableButton.prototype._prevRow = function($tr) {
+    var $prevTr;
+    $prevTr = $tr.prev('tr');
+    if ($prevTr.length < 1 && $tr.parent('tbody').length > 0) {
+      $prevTr = $tr.parent('tbody').prev('thead').find('tr');
+    }
+    return $prevTr;
+  };
+
+  TableButton.prototype.initResize = function($table) {
+    var $colgroup, $editor, $resizeHandle, $wrapper;
+    $wrapper = $table.parent('.simditor-table');
+    $editor = this.editor;
+    $colgroup = $table.find('colgroup');
+    if ($colgroup.length < 1) {
+      $colgroup = $('<colgroup/>').prependTo($table);
+      $table.find('thead tr th').each(function(i, td) {
+        var $col;
+        return $col = $('<col/>').appendTo($colgroup);
+      });
+      this.refreshTableWidth($table);
+    }
+    $resizeHandle = $('<div />', {
+      "class": 'simditor-resize-handle',
+      contenteditable: 'false'
+    }).appendTo($wrapper);
+    $wrapper.on('mousemove', 'td, th', function(e) {
+      var $col, $td, index, ref, ref1, x;
+      if ($wrapper.hasClass('resizing')) {
+        return;
+      }
+      $td = $(e.currentTarget);
+      x = e.pageX - $(e.currentTarget).offset().left;
+      if (x < 5 && $td.prev().length > 0) {
+        $td = $td.prev();
+      }
+      if ($td.next('td, th').length < 1) {
+        $resizeHandle.hide();
+        return;
+      }
+      if ((ref = $resizeHandle.data('td')) != null ? ref.is($td) : void 0) {
+        $resizeHandle.show();
+        return;
+      }
+      index = $td.parent().find('td, th').index($td);
+      $col = $colgroup.find('col').eq(index);
+      if ((ref1 = $resizeHandle.data('col')) != null ? ref1.is($col) : void 0) {
+        $resizeHandle.show();
+        return;
+      }
+      return $resizeHandle.css('left', $td.position().left + $td.outerWidth() - 5).data('td', $td).data('col', $col).show();
+    });
+    $wrapper.on('mouseleave', function(e) {
+      return $resizeHandle.hide();
+    });
+    return $wrapper.on('mousedown', '.simditor-resize-handle', function(e) {
+      var $handle, $leftCol, $leftTd, $rightCol, $rightTd, minWidth, startHandleLeft, startLeftWidth, startRightWidth, startX, tableWidth;
+      $handle = $(e.currentTarget);
+      $leftTd = $handle.data('td');
+      $leftCol = $handle.data('col');
+      $rightTd = $leftTd.next('td, th');
+      $rightCol = $leftCol.next('col');
+      startX = e.pageX;
+      startLeftWidth = $leftTd.outerWidth() * 1;
+      startRightWidth = $rightTd.outerWidth() * 1;
+      startHandleLeft = parseFloat($handle.css('left'));
+      tableWidth = $leftTd.closest('table').width();
+      minWidth = 50;
+      $(document).on('mousemove.simditor-resize-table', function(e) {
+        var deltaX, leftWidth, rightWidth;
+        deltaX = e.pageX - startX;
+        leftWidth = startLeftWidth + deltaX;
+        rightWidth = startRightWidth - deltaX;
+        if (leftWidth < minWidth) {
+          leftWidth = minWidth;
+          deltaX = minWidth - startLeftWidth;
+          rightWidth = startRightWidth - deltaX;
+        } else if (rightWidth < minWidth) {
+          rightWidth = minWidth;
+          deltaX = startRightWidth - minWidth;
+          leftWidth = startLeftWidth + deltaX;
+        }
+        $leftCol.attr('width', (leftWidth / tableWidth * 100) + '%');
+        $rightCol.attr('width', (rightWidth / tableWidth * 100) + '%');
+        return $handle.css('left', startHandleLeft + deltaX);
+      });
+      $(document).one('mouseup.simditor-resize-table', function(e) {
+        $editor.sync();
+        $(document).off('.simditor-resize-table');
+        return $wrapper.removeClass('resizing');
+      });
+      $wrapper.addClass('resizing');
+      return false;
+    });
+  };
+
+  TableButton.prototype._initShortcuts = function() {
+    this.editor.hotkeys.add('ctrl+alt+up', (function(_this) {
+      return function(e) {
+        _this.editMenu.find('.menu-item[data-param=insertRowAbove]').click();
+        return false;
+      };
+    })(this));
+    this.editor.hotkeys.add('ctrl+alt+down', (function(_this) {
+      return function(e) {
+        _this.editMenu.find('.menu-item[data-param=insertRowBelow]').click();
+        return false;
+      };
+    })(this));
+    this.editor.hotkeys.add('ctrl+alt+left', (function(_this) {
+      return function(e) {
+        _this.editMenu.find('.menu-item[data-param=insertColLeft]').click();
+        return false;
+      };
+    })(this));
+    return this.editor.hotkeys.add('ctrl+alt+right', (function(_this) {
+      return function(e) {
+        _this.editMenu.find('.menu-item[data-param=insertColRight]').click();
+        return false;
+      };
+    })(this));
+  };
+
+  TableButton.prototype.decorate = function($table) {
+    var $headRow, $tbody, $thead;
+    if ($table.parent('.simditor-table').length > 0) {
+      this.undecorate($table);
+    }
+    $table.wrap('<div class="simditor-table"></div>');
+    if ($table.find('thead').length < 1) {
+      $thead = $('<thead />');
+      $headRow = $table.find('tr').first();
+      $thead.append($headRow);
+      this._changeCellTag($headRow, 'th');
+      $tbody = $table.find('tbody');
+      if ($tbody.length > 0) {
+        $tbody.before($thead);
+      } else {
+        $table.prepend($thead);
+      }
+    }
+    this.initResize($table);
+    return $table.parent();
+  };
+
+  TableButton.prototype.undecorate = function($table) {
+    if (!($table.parent('.simditor-table').length > 0)) {
+      return;
+    }
+    return $table.parent().replaceWith($table);
+  };
+
+  TableButton.prototype.renderMenu = function() {
+    var $table;
+    $("<div class=\"menu-create-table\">\n</div>\n<div class=\"menu-edit-table\">\n  <ul>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"deleteRow\">\n        <span>" + (this._t('deleteRow')) + "</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"insertRowAbove\">\n        <span>" + (this._t('insertRowAbove')) + " ( Ctrl + Alt + ↑ )</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"insertRowBelow\">\n        <span>" + (this._t('insertRowBelow')) + " ( Ctrl + Alt + ↓ )</span>\n      </a>\n    </li>\n    <li><span class=\"separator\"></span></li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"deleteCol\">\n        <span>" + (this._t('deleteColumn')) + "</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"insertColLeft\">\n        <span>" + (this._t('insertColumnLeft')) + " ( Ctrl + Alt + ← )</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"insertColRight\">\n        <span>" + (this._t('insertColumnRight')) + " ( Ctrl + Alt + → )</span>\n      </a>\n    </li>\n    <li><span class=\"separator\"></span></li>\n    <li>\n      <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n        href=\"javascript:;\" data-param=\"deleteTable\">\n        <span>" + (this._t('deleteTable')) + "</span>\n      </a>\n    </li>\n  </ul>\n</div>").appendTo(this.menuWrapper);
+    this.createMenu = this.menuWrapper.find('.menu-create-table');
+    this.editMenu = this.menuWrapper.find('.menu-edit-table');
+    $table = this.createTable(6, 6).appendTo(this.createMenu);
+    this.createMenu.on('mouseenter', 'td, th', (function(_this) {
+      return function(e) {
+        var $td, $tr, $trs, num;
+        _this.createMenu.find('td, th').removeClass('selected');
+        $td = $(e.currentTarget);
+        $tr = $td.parent();
+        num = $tr.find('td, th').index($td) + 1;
+        $trs = $tr.prevAll('tr').addBack();
+        if ($tr.parent().is('tbody')) {
+          $trs = $trs.add($table.find('thead tr'));
+        }
+        return $trs.find("td:lt(" + num + "), th:lt(" + num + ")").addClass('selected');
+      };
+    })(this));
+    this.createMenu.on('mouseleave', function(e) {
+      return $(e.currentTarget).find('td, th').removeClass('selected');
+    });
+    return this.createMenu.on('mousedown', 'td, th', (function(_this) {
+      return function(e) {
+        var $closestBlock, $td, $tr, colNum, rowNum;
+        _this.wrapper.removeClass('menu-on');
+        if (!_this.editor.inputManager.focused) {
+          return;
+        }
+        $td = $(e.currentTarget);
+        $tr = $td.parent();
+        colNum = $tr.find('td').index($td) + 1;
+        rowNum = $tr.prevAll('tr').length + 1;
+        if ($tr.parent().is('tbody')) {
+          rowNum += 1;
+        }
+        $table = _this.createTable(rowNum, colNum, true);
+        $closestBlock = _this.editor.selection.blockNodes().last();
+        if (_this.editor.util.isEmptyNode($closestBlock)) {
+          $closestBlock.replaceWith($table);
+        } else {
+          $closestBlock.after($table);
+        }
+        _this.decorate($table);
+        _this.editor.selection.setRangeAtStartOf($table.find('th:first'));
+        _this.editor.trigger('valuechanged');
+        return false;
+      };
+    })(this));
+  };
+
+  TableButton.prototype.createTable = function(row, col, phBr) {
+    var $table, $tbody, $td, $thead, $tr, c, k, l, r, ref, ref1;
+    $table = $('<table/>');
+    $thead = $('<thead/>').appendTo($table);
+    $tbody = $('<tbody/>').appendTo($table);
+    for (r = k = 0, ref = row; 0 <= ref ? k < ref : k > ref; r = 0 <= ref ? ++k : --k) {
+      $tr = $('<tr/>');
+      $tr.appendTo(r === 0 ? $thead : $tbody);
+      for (c = l = 0, ref1 = col; 0 <= ref1 ? l < ref1 : l > ref1; c = 0 <= ref1 ? ++l : --l) {
+        $td = $(r === 0 ? '<th/>' : '<td/>').appendTo($tr);
+        if (phBr) {
+          $td.append(this.editor.util.phBr);
+        }
+      }
+    }
+    return $table;
+  };
+
+  TableButton.prototype.refreshTableWidth = function($table) {
+    return setTimeout((function(_this) {
+      return function() {
+        var cols, tableWidth;
+        tableWidth = $table.width();
+        cols = $table.find('col');
+        return $table.find('thead tr th').each(function(i, td) {
+          var $col;
+          $col = cols.eq(i);
+          return $col.attr('width', ($(td).outerWidth() / tableWidth * 100) + '%');
+        });
+      };
+    })(this), 0);
+  };
+
+  TableButton.prototype.setActive = function(active) {
+    TableButton.__super__.setActive.call(this, active);
+    if (active) {
+      this.createMenu.hide();
+      return this.editMenu.show();
+    } else {
+      this.createMenu.show();
+      return this.editMenu.hide();
+    }
+  };
+
+  TableButton.prototype._changeCellTag = function($tr, tagName) {
+    return $tr.find('td, th').each(function(i, cell) {
+      var $cell;
+      $cell = $(cell);
+      return $cell.replaceWith("<" + tagName + ">" + ($cell.html()) + "</" + tagName + ">");
+    });
+  };
+
+  TableButton.prototype.deleteRow = function($td) {
+    var $newTr, $tr, index;
+    $tr = $td.parent('tr');
+    if ($tr.closest('table').find('tr').length < 1) {
+      return this.deleteTable($td);
+    } else {
+      $newTr = this._nextRow($tr);
+      if (!($newTr.length > 0)) {
+        $newTr = this._prevRow($tr);
+      }
+      index = $tr.find('td, th').index($td);
+      if ($tr.parent().is('thead')) {
+        $newTr.appendTo($tr.parent());
+        this._changeCellTag($newTr, 'th');
+      }
+      $tr.remove();
+      return this.editor.selection.setRangeAtEndOf($newTr.find('td, th').eq(index));
+    }
+  };
+
+  TableButton.prototype.insertRow = function($td, direction) {
+    var $newTr, $table, $tr, cellTag, colNum, i, index, k, ref;
+    if (direction == null) {
+      direction = 'after';
+    }
+    $tr = $td.parent('tr');
+    $table = $tr.closest('table');
+    colNum = 0;
+    $table.find('tr').each(function(i, tr) {
+      return colNum = Math.max(colNum, $(tr).find('td').length);
+    });
+    index = $tr.find('td, th').index($td);
+    $newTr = $('<tr/>');
+    cellTag = 'td';
+    if (direction === 'after' && $tr.parent().is('thead')) {
+      $tr.parent().next('tbody').prepend($newTr);
+    } else if (direction === 'before' && $tr.parent().is('thead')) {
+      $tr.before($newTr);
+      $tr.parent().next('tbody').prepend($tr);
+      this._changeCellTag($tr, 'td');
+      cellTag = 'th';
+    } else {
+      $tr[direction]($newTr);
+    }
+    for (i = k = 1, ref = colNum; 1 <= ref ? k <= ref : k >= ref; i = 1 <= ref ? ++k : --k) {
+      $("<" + cellTag + "/>").append(this.editor.util.phBr).appendTo($newTr);
+    }
+    return this.editor.selection.setRangeAtStartOf($newTr.find('td, th').eq(index));
+  };
+
+  TableButton.prototype.deleteCol = function($td) {
+    var $newTd, $table, $tr, index, noOtherCol, noOtherRow;
+    $tr = $td.parent('tr');
+    noOtherRow = $tr.closest('table').find('tr').length < 2;
+    noOtherCol = $td.siblings('td, th').length < 1;
+    if (noOtherRow && noOtherCol) {
+      return this.deleteTable($td);
+    } else {
+      index = $tr.find('td, th').index($td);
+      $newTd = $td.next('td, th');
+      if (!($newTd.length > 0)) {
+        $newTd = $tr.prev('td, th');
+      }
+      $table = $tr.closest('table');
+      $table.find('col').eq(index).remove();
+      $table.find('tr').each(function(i, tr) {
+        return $(tr).find('td, th').eq(index).remove();
+      });
+      this.refreshTableWidth($table);
+      return this.editor.selection.setRangeAtEndOf($newTd);
+    }
+  };
+
+  TableButton.prototype.insertCol = function($td, direction) {
+    var $col, $newCol, $newTd, $table, $tr, index, tableWidth, width;
+    if (direction == null) {
+      direction = 'after';
+    }
+    $tr = $td.parent('tr');
+    index = $tr.find('td, th').index($td);
+    $table = $td.closest('table');
+    $col = $table.find('col').eq(index);
+    $table.find('tr').each((function(_this) {
+      return function(i, tr) {
+        var $newTd, cellTag;
+        cellTag = $(tr).parent().is('thead') ? 'th' : 'td';
+        $newTd = $("<" + cellTag + "/>").append(_this.editor.util.phBr);
+        return $(tr).find('td, th').eq(index)[direction]($newTd);
+      };
+    })(this));
+    $newCol = $('<col/>');
+    $col[direction]($newCol);
+    tableWidth = $table.width();
+    width = Math.max(parseFloat($col.attr('width')) / 2, 50 / tableWidth * 100);
+    $col.attr('width', width + '%');
+    $newCol.attr('width', width + '%');
+    this.refreshTableWidth($table);
+    $newTd = direction === 'after' ? $td.next('td, th') : $td.prev('td, th');
+    return this.editor.selection.setRangeAtStartOf($newTd);
+  };
+
+  TableButton.prototype.deleteTable = function($td) {
+    var $block, $table;
+    $table = $td.closest('.simditor-table');
+    $block = $table.next('p');
+    $table.remove();
+    if ($block.length > 0) {
+      return this.editor.selection.setRangeAtStartOf($block);
+    }
+  };
+
+  TableButton.prototype.command = function(param) {
+    var $td;
+    $td = this.editor.selection.containerNode().closest('td, th');
+    if (!($td.length > 0)) {
+      return;
+    }
+    if (param === 'deleteRow') {
+      this.deleteRow($td);
+    } else if (param === 'insertRowAbove') {
+      this.insertRow($td, 'before');
+    } else if (param === 'insertRowBelow') {
+      this.insertRow($td);
+    } else if (param === 'deleteCol') {
+      this.deleteCol($td);
+    } else if (param === 'insertColLeft') {
+      this.insertCol($td, 'before');
+    } else if (param === 'insertColRight') {
+      this.insertCol($td);
+    } else if (param === 'deleteTable') {
+      this.deleteTable($td);
+    } else {
+      return;
+    }
+    return this.editor.trigger('valuechanged');
+  };
+
+  return TableButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(TableButton);
+
+StrikethroughButton = (function(superClass) {
+  extend(StrikethroughButton, superClass);
+
+  function StrikethroughButton() {
+    return StrikethroughButton.__super__.constructor.apply(this, arguments);
+  }
+
+  StrikethroughButton.prototype.name = 'strikethrough';
+
+  StrikethroughButton.prototype.icon = 'strikethrough';
+
+  StrikethroughButton.prototype.htmlTag = 'strike';
+
+  StrikethroughButton.prototype.disableTag = 'pre';
+
+  StrikethroughButton.prototype._activeStatus = function() {
+    var active;
+    active = document.queryCommandState('strikethrough') === true;
+    this.setActive(active);
+    return this.active;
+  };
+
+  StrikethroughButton.prototype.command = function() {
+    document.execCommand('strikethrough');
+    if (!this.editor.util.support.oninput) {
+      this.editor.trigger('valuechanged');
+    }
+    return $(document).trigger('selectionchange');
+  };
+
+  return StrikethroughButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(StrikethroughButton);
+
+AlignmentButton = (function(superClass) {
+  extend(AlignmentButton, superClass);
+
+  function AlignmentButton() {
+    return AlignmentButton.__super__.constructor.apply(this, arguments);
+  }
+
+  AlignmentButton.prototype.name = "alignment";
+
+  AlignmentButton.prototype.icon = 'align-left';
+
+  AlignmentButton.prototype.htmlTag = 'p, h1, h2, h3, h4, td, th';
+
+  AlignmentButton.prototype._init = function() {
+    this.menu = [
+      {
+        name: 'left',
+        text: this._t('alignLeft'),
+        icon: 'align-left',
+        param: 'left'
+      }, {
+        name: 'center',
+        text: this._t('alignCenter'),
+        icon: 'align-center',
+        param: 'center'
+      }, {
+        name: 'right',
+        text: this._t('alignRight'),
+        icon: 'align-right',
+        param: 'right'
+      }
+    ];
+    return AlignmentButton.__super__._init.call(this);
+  };
+
+  AlignmentButton.prototype.setActive = function(active, align) {
+    if (align == null) {
+      align = 'left';
+    }
+    if (align !== 'left' && align !== 'center' && align !== 'right') {
+      align = 'left';
+    }
+    if (align === 'left') {
+      AlignmentButton.__super__.setActive.call(this, false);
+    } else {
+      AlignmentButton.__super__.setActive.call(this, active);
+    }
+    this.el.removeClass('align-left align-center align-right');
+    if (active) {
+      this.el.addClass('align-' + align);
+    }
+    this.setIcon('align-' + align);
+    return this.menuEl.find('.menu-item').show().end().find('.menu-item-' + align).hide();
+  };
+
+  AlignmentButton.prototype._status = function() {
+    this.nodes = this.editor.selection.nodes().filter(this.htmlTag);
+    if (this.nodes.length < 1) {
+      this.setDisabled(true);
+      return this.setActive(false);
+    } else {
+      this.setDisabled(false);
+      return this.setActive(true, this.nodes.first().css('text-align'));
+    }
+  };
+
+  AlignmentButton.prototype.command = function(align) {
+    if (align !== 'left' && align !== 'center' && align !== 'right') {
+      throw new Error("simditor alignment button: invalid align " + align);
+    }
+    this.nodes.css({
+      'text-align': align === 'left' ? '' : align
+    });
+    this.editor.trigger('valuechanged');
+    return this.editor.inputManager.throttledSelectionChanged();
+  };
+
+  return AlignmentButton;
+
+})(Button);
+
+Simditor.Toolbar.addButton(AlignmentButton);
+
+return Simditor;
+
+}));

+ 261 - 0
addons/simditor/src/js/uploader.js

@@ -0,0 +1,261 @@
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module unless amdModuleId is set
+    define('simple-uploader', ["jquery","simple-module"], function ($, SimpleModule) {
+      return (root['uploader'] = factory($, SimpleModule));
+    });
+  } else if (typeof exports === 'object') {
+    // Node. Does not work with strict CommonJS, but
+    // only CommonJS-like environments that support module.exports,
+    // like Node.
+    module.exports = factory(require("jquery"),require("simple-module"));
+  } else {
+    root.simple = root.simple || {};
+    root.simple['uploader'] = factory(jQuery,SimpleModule);
+  }
+}(this, function ($, SimpleModule) {
+
+var Uploader, uploader,
+  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+  hasProp = {}.hasOwnProperty;
+
+Uploader = (function(superClass) {
+  extend(Uploader, superClass);
+
+  function Uploader() {
+    return Uploader.__super__.constructor.apply(this, arguments);
+  }
+
+  Uploader.count = 0;
+
+  Uploader.prototype.opts = {
+    url: '',
+    params: null,
+    fileKey: 'upload_file',
+    connectionCount: 3
+  };
+
+  Uploader.prototype._init = function() {
+    this.files = [];
+    this.queue = [];
+    this.id = ++Uploader.count;
+    this.on('uploadcomplete', (function(_this) {
+      return function(e, file) {
+        _this.files.splice($.inArray(file, _this.files), 1);
+        if (_this.queue.length > 0 && _this.files.length < _this.opts.connectionCount) {
+          return _this.upload(_this.queue.shift());
+        } else if (_this.files.length === 0) {
+          return _this.uploading = false;
+        }
+      };
+    })(this));
+    return $(window).on('beforeunload.uploader-' + this.id, (function(_this) {
+      return function(e) {
+        if (!_this.uploading) {
+          return;
+        }
+        e.originalEvent.returnValue = _this._t('leaveConfirm');
+        return _this._t('leaveConfirm');
+      };
+    })(this));
+  };
+
+  Uploader.prototype.generateId = (function() {
+    var id;
+    id = 0;
+    return function() {
+      return id += 1;
+    };
+  })();
+
+  Uploader.prototype.upload = function(file, opts) {
+    var f, i, key, len;
+    if (opts == null) {
+      opts = {};
+    }
+    if (file == null) {
+      return;
+    }
+    if ($.isArray(file) || file instanceof FileList) {
+      for (i = 0, len = file.length; i < len; i++) {
+        f = file[i];
+        this.upload(f, opts);
+      }
+    } else if ($(file).is('input:file')) {
+      key = $(file).attr('name');
+      if (key) {
+        opts.fileKey = key;
+      }
+      this.upload($.makeArray($(file)[0].files), opts);
+    } else if (!file.id || !file.obj) {
+      file = this.getFile(file);
+    }
+    if (!(file && file.obj)) {
+      return;
+    }
+    $.extend(file, opts);
+    if (this.files.length >= this.opts.connectionCount) {
+      this.queue.push(file);
+      return;
+    }
+    if (this.triggerHandler('beforeupload', [file]) === false) {
+      return;
+    }
+    this.files.push(file);
+    this._xhrUpload(file);
+    return this.uploading = true;
+  };
+
+  Uploader.prototype.getFile = function(fileObj) {
+    var name, ref, ref1;
+    if (fileObj instanceof window.File || fileObj instanceof window.Blob) {
+      name = (ref = fileObj.fileName) != null ? ref : fileObj.name;
+    } else {
+      return null;
+    }
+    return {
+      id: this.generateId(),
+      url: this.opts.url,
+      params: this.opts.params,
+      fileKey: this.opts.fileKey,
+      name: name,
+      size: (ref1 = fileObj.fileSize) != null ? ref1 : fileObj.size,
+      ext: name ? name.split('.').pop().toLowerCase() : '',
+      obj: fileObj
+    };
+  };
+
+  Uploader.prototype._xhrUpload = function(file) {
+    var formData, k, ref, v;
+    formData = new FormData();
+    formData.append(file.fileKey, file.obj);
+    formData.append("original_filename", file.name);
+    if (file.params) {
+      ref = file.params;
+      for (k in ref) {
+        v = ref[k];
+        formData.append(k, v);
+      }
+    }
+    return file.xhr = $.ajax({
+      url: file.url,
+      data: formData,
+      processData: false,
+      contentType: false,
+      type: 'POST',
+      headers: {
+        'X-File-Name': encodeURIComponent(file.name)
+      },
+      xhr: function() {
+        var req;
+        req = $.ajaxSettings.xhr();
+        if (req) {
+          req.upload.onprogress = (function(_this) {
+            return function(e) {
+              return _this.progress(e);
+            };
+          })(this);
+        }
+        return req;
+      },
+      progress: (function(_this) {
+        return function(e) {
+          if (!e.lengthComputable) {
+            return;
+          }
+          return _this.trigger('uploadprogress', [file, e.loaded, e.total]);
+        };
+      })(this),
+      error: (function(_this) {
+        return function(xhr, status, err) {
+          return _this.trigger('uploaderror', [file, xhr, status]);
+        };
+      })(this),
+      success: (function(_this) {
+        return function(result) {
+          _this.trigger('uploadprogress', [file, file.size, file.size]);
+          _this.trigger('uploadsuccess', [file, result]);
+          return $(document).trigger('uploadsuccess', [file, result, _this]);
+        };
+      })(this),
+      complete: (function(_this) {
+        return function(xhr, status) {
+          return _this.trigger('uploadcomplete', [file, xhr.responseText]);
+        };
+      })(this)
+    });
+  };
+
+  Uploader.prototype.cancel = function(file) {
+    var f, i, len, ref;
+    if (!file.id) {
+      ref = this.files;
+      for (i = 0, len = ref.length; i < len; i++) {
+        f = ref[i];
+        if (f.id === file * 1) {
+          file = f;
+          break;
+        }
+      }
+    }
+    this.trigger('uploadcancel', [file]);
+    if (file.xhr) {
+      file.xhr.abort();
+    }
+    return file.xhr = null;
+  };
+
+  Uploader.prototype.readImageFile = function(fileObj, callback) {
+    var fileReader, img;
+    if (!$.isFunction(callback)) {
+      return;
+    }
+    img = new Image();
+    img.onload = function() {
+      return callback(img);
+    };
+    img.onerror = function() {
+      return callback();
+    };
+    if (window.FileReader && FileReader.prototype.readAsDataURL && /^image/.test(fileObj.type)) {
+      fileReader = new FileReader();
+      fileReader.onload = function(e) {
+        return img.src = e.target.result;
+      };
+      return fileReader.readAsDataURL(fileObj);
+    } else {
+      return callback();
+    }
+  };
+
+  Uploader.prototype.destroy = function() {
+    var file, i, len, ref;
+    this.queue.length = 0;
+    ref = this.files;
+    for (i = 0, len = ref.length; i < len; i++) {
+      file = ref[i];
+      this.cancel(file);
+    }
+    $(window).off('.uploader-' + this.id);
+    return $(document).off('.uploader-' + this.id);
+  };
+
+  Uploader.i18n = {
+    'zh-CN': {
+      leaveConfirm: '正在上传文件,如果离开上传会自动取消'
+    }
+  };
+
+  Uploader.locale = 'zh-CN';
+
+  return Uploader;
+
+})(SimpleModule);
+
+uploader = function(opts) {
+  return new Uploader(opts);
+};
+
+return uploader;
+
+}));

+ 181 - 0
application/admin/controller/commodity/Commodity.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace app\admin\controller\commodity;
+
+use app\common\controller\Backend;
+use think\Db;
+use think\Exception;
+use think\exception\PDOException;
+use think\exception\ValidateException;
+
+/**
+ * 
+ *
+ * @icon fa fa-circle-o
+ */
+class Commodity extends Backend
+{
+    
+    /**
+     * Commodity模型对象
+     * @var \app\admin\model\commodity\Commodity
+     */
+    protected $model = null;
+    protected $multiFields = 'c_state_switch,c_recommodity';
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\commodity\Commodity;
+
+    }
+    
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+    
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        //当前是否为关联查询
+        $this->relationSearch = false;
+        //设置过滤方法
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax())
+        {
+            //如果发送的来源是Selectpage,则转发到Selectpage
+            if ($this->request->request('keyField'))
+            {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+            $total = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->count();
+
+            $list = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->limit($offset, $limit)
+                    ->select();
+
+            foreach ($list as $row) {
+                $row->visible(['c_id','c_name','c_notice_content','c_title_content','c_images','c_yuanjiaprice','c_vipprice','c_freight','c_buynum','c_stock','c_type','c_whitebean','c_sort','c_recommodity','c_state_switch']);
+                
+            }
+            $list = collection($list)->toArray();
+            $result = array("total" => $total, "rows" => $list);
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+    /**
+     * 添加
+     */
+    public function add()
+    {
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $params = $this->preExcludeFields($params);
+                halt($params);
+                if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
+                    $params[$this->dataLimitField] = $this->auth->id;
+                }
+                $result = false;
+                Db::startTrans();
+                try {
+                    //是否采用模型验证
+                    if ($this->modelValidate) {
+                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
+                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
+                        $this->model->validateFailException(true)->validate($validate);
+                    }
+                    $result = $this->model->allowField(true)->save($params);
+                    Db::commit();
+                } catch (ValidateException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $ctype = Db::name('ctype')->select();
+        return $this->view->fetch('add', ['ctype' => $ctype]);
+    }
+    /**
+     * 编辑
+     */
+    public function edit($ids = null)
+    {
+        $row = $this->model->get($ids);
+        if (!$row) {
+            $this->error(__('No Results were found'));
+        }
+        $adminIds = $this->getDataLimitAdminIds();
+        if (is_array($adminIds)) {
+            if (!in_array($row[$this->dataLimitField], $adminIds)) {
+                $this->error(__('You have no permission'));
+            }
+        }
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+
+            if ($params) {
+                $params = $this->preExcludeFields($params);
+                $result = false;
+                Db::startTrans();
+                try {
+                    //是否采用模型验证
+                    if ($this->modelValidate) {
+                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
+                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
+                        $row->validateFailException(true)->validate($validate);
+                    }
+                    $result = $row->allowField(true)->save($params);
+                    Db::commit();
+                } catch (ValidateException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were updated'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $this->view->assign("row", $row);
+        $ctype = Db::name('ctype')->select();
+        $this->view->assign("ctype", $ctype);
+        return $this->view->fetch();
+    }
+    public function commodityInfo ($ids='') {
+    }
+}

+ 76 - 0
application/admin/controller/ctype/Ctype.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace app\admin\controller\ctype;
+
+use app\common\controller\Backend;
+
+/**
+ * 
+ *
+ * @icon fa fa-circle-o
+ */
+class Ctype extends Backend
+{
+    
+    /**
+     * Ctype模型对象
+     * @var \app\admin\model\ctype\Ctype
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\ctype\Ctype;
+
+    }
+    
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+    
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        //当前是否为关联查询
+        $this->relationSearch = false;
+        //设置过滤方法
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax())
+        {
+            //如果发送的来源是Selectpage,则转发到Selectpage
+            if ($this->request->request('keyField'))
+            {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+            $total = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->count();
+
+            $list = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->limit($offset, $limit)
+                    ->select();
+
+            foreach ($list as $row) {
+                $row->visible(['t_id','t_name']);
+                
+            }
+            $list = collection($list)->toArray();
+            $result = array("total" => $total, "rows" => $list);
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+}

+ 40 - 0
application/admin/controller/test/Test.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace app\admin\controller\test;
+
+use app\common\controller\Backend;
+
+/**
+ * 测试管理
+ *
+ * @icon fa fa-circle-o
+ */
+class Test extends Backend
+{
+    
+    /**
+     * Test模型对象
+     * @var \app\admin\model\test\Test
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\test\Test;
+        $this->view->assign("weekList", $this->model->getWeekList());
+        $this->view->assign("flagList", $this->model->getFlagList());
+        $this->view->assign("genderdataList", $this->model->getGenderdataList());
+        $this->view->assign("hobbydataList", $this->model->getHobbydataList());
+        $this->view->assign("statusList", $this->model->getStatusList());
+        $this->view->assign("stateList", $this->model->getStateList());
+    }
+    
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+    
+
+}

+ 0 - 1
application/admin/controller/user/User.php

@@ -71,5 +71,4 @@ class User extends Backend
         $this->view->assign('groupList', build_select('row[group_id]', \app\admin\model\UserGroup::column('id,name'), $row['group_id'], ['class' => 'form-control selectpicker']));
         return parent::edit($ids);
     }
-
 }

+ 173 - 0
application/admin/controller/users/Users.php

@@ -0,0 +1,173 @@
+<?php
+
+namespace app\admin\controller\users;
+
+use app\common\controller\Backend;
+use think\Db;
+use think\Exception;
+use think\exception\PDOException;
+use think\exception\ValidateException;
+
+/**
+ * 
+ *
+ * @icon fa fa-users
+ */
+class Users extends Backend
+{
+    
+    /**
+     * Users模型对象
+     * @var \app\admin\model\users\Users
+     */
+    protected $model = null;
+    protected  $multiFields = 'status_switch';
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\users\Users;
+
+    }
+    
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+    
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        //当前是否为关联查询
+        $this->relationSearch = false;
+        //设置过滤方法
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax())
+        {
+            //如果发送的来源是Selectpage,则转发到Selectpage
+            if ($this->request->request('keyField'))
+            {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+            $total = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->count();
+
+            $list = $this->model
+                    
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->limit($offset, $limit)
+                    ->select();
+
+            foreach ($list as $row) {
+                $row->visible(['user_id','user_nickname','user_tel','user_avatar','user_level','user_money','user_redbean','user_whitebean','user_line_num','create_time','update_time','status_switch']);
+                
+            }
+            $list = collection($list)->toArray();
+            $result = array("total" => $total, "rows" => $list);
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+    /**
+     * 编辑
+     */
+    public function edit($ids = null)
+    {
+        $row = $this->model->get($ids);
+        if (!$row) {
+            $this->error(__('No Results were found'));
+        }
+        $adminIds = $this->getDataLimitAdminIds();
+        if (is_array($adminIds)) {
+            if (!in_array($row[$this->dataLimitField], $adminIds)) {
+                $this->error(__('You have no permission'));
+            }
+        }
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $params = $this->preExcludeFields($params);
+                $result = false;
+                Db::startTrans();
+                try {
+                    //是否采用模型验证
+                    if ($this->modelValidate) {
+                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
+                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
+                        $row->validateFailException(true)->validate($validate);
+                    }
+                    $result = $row->allowField(true)->save($params);
+                    Db::commit();
+                } catch (ValidateException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were updated'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $this->view->assign("row", $row);
+        return $this->view->fetch();
+    }
+    /**
+     * 批量更新
+     */
+    public function multi($ids = "")
+    {
+        $ids = $ids ? $ids : $this->request->param("ids");
+        if ($ids) {
+            if ($this->request->has('params')) {
+                parse_str($this->request->post("params"), $values);
+                $values = array_intersect_key($values, array_flip(is_array($this->multiFields) ? $this->multiFields : explode(',', $this->multiFields)));
+                if ($values || $this->auth->isSuperAdmin()) {
+                    $adminIds = $this->getDataLimitAdminIds();
+                    if (is_array($adminIds)) {
+                        $this->model->where($this->dataLimitField, 'in', $adminIds);
+                    }
+                    $count = 0;
+                    Db::startTrans();
+                    try {
+                        $list = $this->model->where('user_id', 'in', $ids)->select();
+                        foreach ($list as $index => $item) {
+                            $count += $item->allowField(true)->isUpdate(true)->save($values);
+                        }
+                        Db::commit();
+                    } catch (PDOException $e) {
+                        Db::rollback();
+                        $this->error($e->getMessage());
+                    } catch (Exception $e) {
+                        Db::rollback();
+                        $this->error($e->getMessage());
+                    }
+                    if ($count) {
+                        $this->success();
+                    } else {
+                        $this->error(__('No rows were updated'));
+                    }
+                } else {
+                    $this->error(__('You have no permission'));
+                }
+            }
+        }
+        $this->error(__('Parameter %s can not be empty', 'ids'));
+    }
+}

+ 19 - 0
application/admin/lang/zh-cn/commodity/commodity.php

@@ -0,0 +1,19 @@
+<?php
+
+return [
+    'C_id'             => '商品id',
+    'C_name'           => '商品名',
+    'C_notice_content' => '商品详情',
+    'C_title_content'  => '商品标题',
+    'C_images'         => '商品图',
+    'C_yuanjiaprice'   => '未打折价格',
+    'C_vipprice'       => '出售价格',
+    'C_freight'        => '运费',
+    'C_buynum'         => '销量',
+    'C_stock'          => '库存',
+    'C_type'           => '商品类型',
+    'C_whitebean'      => '赠送白豆',
+    'C_sort'           => '排序',
+    'C_recommodity'    => '推荐商品0不是1是',
+    'C_state_switch'   => '状态'
+];

+ 6 - 0
application/admin/lang/zh-cn/ctype/ctype.php

@@ -0,0 +1,6 @@
+<?php
+
+return [
+    'T_id'   => 'l商品类型id',
+    'T_name' => '类型名'
+];

+ 51 - 0
application/admin/lang/zh-cn/test/test.php

@@ -0,0 +1,51 @@
+<?php
+
+return [
+    'Id'                 => 'ID',
+    'Admin_id'           => '管理员ID',
+    'Category_id'        => '分类ID(单选)',
+    'Category_ids'       => '分类ID(多选)',
+    'Week'               => '星期(单选)',
+    'Week monday'        => '星期一',
+    'Week tuesday'       => '星期二',
+    'Week wednesday'     => '星期三',
+    'Flag'               => '标志(多选)',
+    'Flag hot'           => '热门',
+    'Flag index'         => '首页',
+    'Flag recommend'     => '推荐',
+    'Genderdata'         => '性别(单选)',
+    'Genderdata male'    => '男',
+    'Genderdata female'  => '女',
+    'Hobbydata'          => '爱好(多选)',
+    'Hobbydata music'    => '音乐',
+    'Hobbydata reading'  => '读书',
+    'Hobbydata swimming' => '游泳',
+    'Title'              => '标题',
+    'Content'            => '内容',
+    'Image'              => '图片',
+    'Images'             => '图片组',
+    'Attachfile'         => '附件',
+    'Keywords'           => '关键字',
+    'Description'        => '描述',
+    'City'               => '省市',
+    'Json'               => '配置',
+    'Json key'           => '名称',
+    'Json value'         => '值',
+    'Price'              => '价格',
+    'Views'              => '点击',
+    'Startdate'          => '开始日期',
+    'Activitytime'       => '活动时间(datetime)',
+    'Year'               => '年',
+    'Times'              => '时间',
+    'Refreshtime'        => '刷新时间(int)',
+    'Createtime'         => '创建时间',
+    'Updatetime'         => '更新时间',
+    'Deletetime'         => '删除时间',
+    'Weigh'              => '权重',
+    'Switch'             => '开关',
+    'Status'             => '状态',
+    'State'              => '状态值',
+    'State 0'            => '禁用',
+    'State 1'            => '正常',
+    'State 2'            => '推荐'
+];

+ 16 - 0
application/admin/lang/zh-cn/users/users.php

@@ -0,0 +1,16 @@
+<?php
+
+return [
+    'User_id'        => '用户id',
+    'User_nickname'  => '姓名',
+    'User_tel'       => '手机号',
+    'User_avatar'    => '头像',
+    'User_level'     => '等级1普通会员2VIP3铂金会员4钻石会员',
+    'User_money'     => '余额',
+    'User_redbean'   => '红米',
+    'User_whitebean' => '白米',
+    'User_line_num'  => '排队剩余次数',
+    'Create_time'    => '创建时间',
+    'Update_time'    => '修改时间',
+    'Status_switch'  => '0封号1正常'
+];

+ 59 - 0
application/admin/model/Command.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace app\admin\model;
+
+use think\Model;
+
+class Command extends Model
+{
+    // 表名
+    protected $name = 'command';
+
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = 'int';
+
+    // 定义时间戳字段名
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+
+    // 追加属性
+    protected $append = [
+        'executetime_text',
+        'type_text',
+        'status_text'
+    ];
+
+
+    public function getStatusList()
+    {
+        return ['successed' => __('Successed'), 'failured' => __('Failured')];
+    }
+
+
+    public function getExecutetimeTextAttr($value, $data)
+    {
+        $value = $value ? $value : $data['executetime'];
+        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
+    }
+
+    public function getTypeTextAttr($value, $data)
+    {
+        $value = $value ? $value : $data['type'];
+        $list = ['crud' => '一键生成CRUD', 'menu' => '一键生成菜单', 'min' => '一键压缩打包', 'api' => '一键生成文档'];
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+    public function getStatusTextAttr($value, $data)
+    {
+        $value = $value ? $value : $data['status'];
+        $list = $this->getStatusList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+    protected function setExecutetimeAttr($value)
+    {
+        return $value && !is_numeric($value) ? strtotime($value) : $value;
+    }
+
+
+}

+ 150 - 0
application/admin/model/test/Test.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace app\admin\model\test;
+
+use think\Model;
+use traits\model\SoftDelete;
+
+class Test extends Model
+{
+
+    use SoftDelete;
+
+    
+
+    // 表名
+    protected $name = 'test';
+    
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = 'int';
+
+    // 定义时间戳字段名
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    // 追加属性
+    protected $append = [
+        'week_text',
+        'flag_text',
+        'genderdata_text',
+        'hobbydata_text',
+        'refreshtime_text',
+        'status_text',
+        'state_text'
+    ];
+    
+
+    protected static function init()
+    {
+        self::afterInsert(function ($row) {
+            $pk = $row->getPk();
+            $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
+        });
+    }
+
+    
+    public function getWeekList()
+    {
+        return ['monday' => __('Week monday'), 'tuesday' => __('Week tuesday'), 'wednesday' => __('Week wednesday')];
+    }
+
+    public function getFlagList()
+    {
+        return ['hot' => __('Flag hot'), 'index' => __('Flag index'), 'recommend' => __('Flag recommend')];
+    }
+
+    public function getGenderdataList()
+    {
+        return ['male' => __('Genderdata male'), 'female' => __('Genderdata female')];
+    }
+
+    public function getHobbydataList()
+    {
+        return ['music' => __('Hobbydata music'), 'reading' => __('Hobbydata reading'), 'swimming' => __('Hobbydata swimming')];
+    }
+
+    public function getStatusList()
+    {
+        return ['normal' => __('Normal'), 'hidden' => __('Hidden')];
+    }
+
+    public function getStateList()
+    {
+        return ['0' => __('State 0'), '1' => __('State 1'), '2' => __('State 2')];
+    }
+
+
+    public function getWeekTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['week']) ? $data['week'] : '');
+        $list = $this->getWeekList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+
+    public function getFlagTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['flag']) ? $data['flag'] : '');
+        $valueArr = explode(',', $value);
+        $list = $this->getFlagList();
+        return implode(',', array_intersect_key($list, array_flip($valueArr)));
+    }
+
+
+    public function getGenderdataTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['genderdata']) ? $data['genderdata'] : '');
+        $list = $this->getGenderdataList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+
+    public function getHobbydataTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['hobbydata']) ? $data['hobbydata'] : '');
+        $valueArr = explode(',', $value);
+        $list = $this->getHobbydataList();
+        return implode(',', array_intersect_key($list, array_flip($valueArr)));
+    }
+
+
+    public function getRefreshtimeTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['refreshtime']) ? $data['refreshtime'] : '');
+        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
+    }
+
+
+    public function getStatusTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
+        $list = $this->getStatusList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+
+    public function getStateTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['state']) ? $data['state'] : '');
+        $list = $this->getStateList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
+    protected function setFlagAttr($value)
+    {
+        return is_array($value) ? implode(',', $value) : $value;
+    }
+
+    protected function setHobbydataAttr($value)
+    {
+        return is_array($value) ? implode(',', $value) : $value;
+    }
+
+    protected function setRefreshtimeAttr($value)
+    {
+        return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
+    }
+
+
+}

+ 61 - 0
application/admin/model/users/Users.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace app\admin\model\users;
+
+use think\Model;
+
+
+class Users extends Model
+{
+
+    
+
+    
+
+    // 表名
+    protected $name = 'users';
+    
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = false;
+
+    // 定义时间戳字段名
+    protected $createTime = false;
+    protected $updateTime = false;
+    protected $deleteTime = false;
+
+    // 追加属性
+    protected $append = [
+        'create_time_text',
+        'update_time_text'
+    ];
+    
+
+    
+
+
+
+    public function getCreateTimeTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['create_time']) ? $data['create_time'] : '');
+        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
+    }
+
+
+    public function getUpdateTimeTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['update_time']) ? $data['update_time'] : '');
+        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
+    }
+
+    protected function setCreateTimeAttr($value)
+    {
+        return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
+    }
+
+    protected function setUpdateTimeAttr($value)
+    {
+        return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
+    }
+
+
+}

+ 95 - 0
application/admin/view/commodity/commodity/add.html

@@ -0,0 +1,95 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_name" data-rule="required" class="form-control" name="row[c_name]" type="text">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_notice_content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-c_notice_content" data-rule="required" class="form-control editor" rows="5" name="row[c_notice_content]" cols="50"></textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_title_content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-c_title_content" data-rule="required" class="form-control editor" rows="5" name="row[c_title_content]" cols="50"></textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_images')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-c_images" data-rule="required" class="form-control" size="50" name="row[c_images]" type="text">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-c_images" class="btn btn-danger plupload" data-input-id="c-c_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-c_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-c_images" class="btn btn-primary fachoose" data-input-id="c-c_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-c_images"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-c_images"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_yuanjiaprice')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_yuanjiaprice" data-rule="required" class="form-control" step="0.01" name="row[c_yuanjiaprice]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_vipprice')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_vipprice" data-rule="required" class="form-control" step="0.01" name="row[c_vipprice]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_freight')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_freight" data-rule="required" class="form-control" step="0.01" name="row[c_freight]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_buynum')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_buynum" data-rule="required" class="form-control" name="row[c_buynum]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_stock')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_stock" data-rule="required" class="form-control" name="row[c_stock]" type="number" value="1">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_type')}:</label>
+        <div class="col-xs-12 col-sm-8">
+<!--            <input id="c-c_type" data-rule="required" class="form-control" name="row[c_type]" type="number" value="0">-->
+            <select id="c-c_type" data-rule="required" class="form-control" name="row[c_type]">
+                {volist name='ctype', id='item'}
+                <option value="{$item.t_id}">{$item.t_name}</option>
+                {/volist}
+            </select>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_whitebean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_whitebean" class="form-control" name="row[c_whitebean]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_sort')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_sort" class="form-control" name="row[c_sort]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 96 - 0
application/admin/view/commodity/commodity/edit.html

@@ -0,0 +1,96 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_name" data-rule="required" class="form-control" name="row[c_name]" type="text" value="{$row.c_name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_notice_content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-c_notice_content" data-rule="required" class="form-control editor" rows="5" name="row[c_notice_content]" cols="50">{$row.c_notice_content|htmlentities}</textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_title_content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-c_title_content" data-rule="required" class="form-control editor" rows="5" name="row[c_title_content]" cols="50">{$row.c_title_content|htmlentities}</textarea>
+        </div>
+    </div>
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_images')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-c_images" data-rule="required" class="form-control" size="50" name="row[c_images]" type="text" value="{$row.c_images|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-c_images" class="btn btn-danger plupload" data-input-id="c-c_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-c_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-c_images" class="btn btn-primary fachoose" data-input-id="c-c_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-c_images"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-c_images"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_yuanjiaprice')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_yuanjiaprice" data-rule="required" class="form-control" step="0.01" name="row[c_yuanjiaprice]" type="number" value="{$row.c_yuanjiaprice|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_vipprice')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_vipprice" data-rule="required" class="form-control" step="0.01" name="row[c_vipprice]" type="number" value="{$row.c_vipprice|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_freight')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_freight" data-rule="required" class="form-control" step="0.01" name="row[c_freight]" type="number" value="{$row.c_freight|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_buynum')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_buynum" data-rule="required" class="form-control" name="row[c_buynum]" type="number" value="{$row.c_buynum|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_stock')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_stock" data-rule="required" class="form-control" name="row[c_stock]" type="number" value="{$row.c_stock|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_type')}:</label>
+        <div class="col-xs-12 col-sm-8">
+<!--            <input id="c-c_type" data-rule="required" class="form-control" name="row[c_type]" type="number" value="{$row.c_type|htmlentities}">-->
+                <select id="c-c_type" data-rule="required" class="form-control" name="row[c_type]">
+                {volist name="ctype" id="item"}
+                    <option value="{$item.t_id}" {if $item.t_id==$row.c_type } selected {/if} >{$item.t_name}</option>
+                {/volist}
+                </select>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_whitebean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_whitebean" class="form-control" name="row[c_whitebean]" type="number" value="{$row.c_whitebean|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('C_sort')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-c_sort" class="form-control" name="row[c_sort]" type="number" value="{$row.c_sort|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 35 - 0
application/admin/view/commodity/commodity/index.html

@@ -0,0 +1,35 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('commodity/commodity/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('commodity/commodity/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('commodity/commodity/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('commodity/commodity/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
+
+                        <div class="dropdown btn-group {:$auth->check('commodity/commodity/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
+                            </ul>
+                        </div>
+
+                        
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('commodity/commodity/edit')}" 
+                           data-operate-del="{:$auth->check('commodity/commodity/del')}" 
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 16 - 0
application/admin/view/ctype/ctype/add.html

@@ -0,0 +1,16 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('T_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-t_name" data-rule="required" class="form-control" name="row[t_name]" type="text">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 16 - 0
application/admin/view/ctype/ctype/edit.html

@@ -0,0 +1,16 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('T_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-t_name" data-rule="required" class="form-control" name="row[t_name]" type="text" value="{$row.t_name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 35 - 0
application/admin/view/ctype/ctype/index.html

@@ -0,0 +1,35 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('ctype/ctype/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('ctype/ctype/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('ctype/ctype/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('ctype/ctype/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
+
+                        <div class="dropdown btn-group {:$auth->check('ctype/ctype/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
+                            </ul>
+                        </div>
+
+                        
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('ctype/ctype/edit')}" 
+                           data-operate-del="{:$auth->check('ctype/ctype/del')}" 
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 240 - 0
application/admin/view/test/test/add.html

@@ -0,0 +1,240 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Category_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-category_id" data-rule="required" data-source="category/selectpage" data-params='{"custom[type]":"test"}' class="form-control selectpage" name="row[category_id]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Category_ids')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-category_ids" data-rule="required" data-source="category/selectpage" data-params='{"custom[type]":"test"}' data-multiple="true" class="form-control selectpage" name="row[category_ids]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Week')}:</label>
+        <div class="col-xs-12 col-sm-8">
+                        
+            <select  id="c-week" data-rule="required" class="form-control selectpicker" name="row[week]">
+                {foreach name="weekList" item="vo"}
+                    <option value="{$key}" {in name="key" value=""}selected{/in}>{$vo}</option>
+                {/foreach}
+            </select>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Flag')}:</label>
+        <div class="col-xs-12 col-sm-8">
+                        
+            <select  id="c-flag" data-rule="required" class="form-control selectpicker" multiple="" name="row[flag][]">
+                {foreach name="flagList" item="vo"}
+                    <option value="{$key}" {in name="key" value=""}selected{/in}>{$vo}</option>
+                {/foreach}
+            </select>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Genderdata')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="genderdataList" item="vo"}
+            <label for="row[genderdata]-{$key}"><input id="row[genderdata]-{$key}" name="row[genderdata]" type="radio" value="{$key}" {in name="key" value="male"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Hobbydata')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="checkbox">
+            {foreach name="hobbydataList" item="vo"}
+            <label for="row[hobbydata][]-{$key}"><input id="row[hobbydata][]-{$key}" name="row[hobbydata][]" type="checkbox" value="{$key}" {in name="key" value=""}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-content" data-rule="required" class="form-control editor" rows="5" name="row[content]" cols="50"></textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Image')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-image" data-rule="required" class="form-control" size="50" name="row[image]" type="text" value="">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-image" class="btn btn-danger plupload" data-input-id="c-image" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-input-id="c-image" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-image"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-image"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-images" data-rule="required" class="form-control" size="50" name="row[images]" type="text" value="">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-images" class="btn btn-danger plupload" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-images" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-images"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-images"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Attachfile')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-attachfile" data-rule="required" class="form-control" size="50" name="row[attachfile]" type="text" value="">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-attachfile" class="btn btn-danger plupload" data-input-id="c-attachfile" data-multiple="false" data-preview-id="p-attachfile"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-attachfile" class="btn btn-primary fachoose" data-input-id="c-attachfile" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-attachfile"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-attachfile"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Keywords')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-keywords" data-rule="required" class="form-control" name="row[keywords]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Description')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-description" data-rule="required" class="form-control" name="row[description]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('City')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class='control-relative'><input id="c-city" data-rule="required" class="form-control" data-toggle="city-picker" name="row[city]" type="text" value=""></div>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Json')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <dl class="fieldlist" data-name="row[json]">
+                <dd>
+                    <ins>{:__('Json key')}</ins>
+                    <ins>{:__('Json value')}</ins>
+                </dd>
+                <dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
+                <textarea name="row[json]" class="form-control hide" cols="30" rows="5"></textarea>
+            </dl>
+
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Price')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-price" data-rule="required" class="form-control" step="0.01" name="row[price]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Views')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-views" data-rule="required" class="form-control" name="row[views]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Startdate')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-startdate" class="form-control datetimepicker" data-date-format="YYYY-MM-DD" data-use-current="true" name="row[startdate]" type="text" value="{:date('Y-m-d')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Activitytime')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-activitytime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[activitytime]" type="text" value="{:date('Y-m-d H:i:s')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Year')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-year" class="form-control datetimepicker" data-date-format="YYYY" data-use-current="true" name="row[year]" type="text" value="{:date('Y')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Times')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-times" class="form-control datetimepicker" data-date-format="HH:mm:ss" data-use-current="true" name="row[times]" type="text" value="{:date('H:i:s')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Refreshtime')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-refreshtime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[refreshtime]" type="text" value="{:date('Y-m-d H:i:s')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" data-rule="required" class="form-control" name="row[weigh]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Switch')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <input  id="c-switch" name="row[switch]" type="hidden" value="0">
+            <a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-switch" data-yes="1" data-no="0" >
+                <i class="fa fa-toggle-on text-success fa-flip-horizontal text-gray fa-2x"></i>
+            </a>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="statusList" item="vo"}
+            <label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="normal"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('State')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="stateList" item="vo"}
+            <label for="row[state]-{$key}"><input id="row[state]-{$key}" name="row[state]" type="radio" value="{$key}" {in name="key" value="1"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 240 - 0
application/admin/view/test/test/edit.html

@@ -0,0 +1,240 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Category_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-category_id" data-rule="required" data-source="category/selectpage" data-params='{"custom[type]":"test"}' class="form-control selectpage" name="row[category_id]" type="text" value="{$row.category_id|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Category_ids')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-category_ids" data-rule="required" data-source="category/selectpage" data-params='{"custom[type]":"test"}' data-multiple="true" class="form-control selectpage" name="row[category_ids]" type="text" value="{$row.category_ids|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Week')}:</label>
+        <div class="col-xs-12 col-sm-8">
+                        
+            <select  id="c-week" data-rule="required" class="form-control selectpicker" name="row[week]">
+                {foreach name="weekList" item="vo"}
+                    <option value="{$key}" {in name="key" value="$row.week"}selected{/in}>{$vo}</option>
+                {/foreach}
+            </select>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Flag')}:</label>
+        <div class="col-xs-12 col-sm-8">
+                        
+            <select  id="c-flag" data-rule="required" class="form-control selectpicker" multiple="" name="row[flag][]">
+                {foreach name="flagList" item="vo"}
+                    <option value="{$key}" {in name="key" value="$row.flag"}selected{/in}>{$vo}</option>
+                {/foreach}
+            </select>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Genderdata')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="genderdataList" item="vo"}
+            <label for="row[genderdata]-{$key}"><input id="row[genderdata]-{$key}" name="row[genderdata]" type="radio" value="{$key}" {in name="key" value="$row.genderdata"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Hobbydata')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="checkbox">
+            {foreach name="hobbydataList" item="vo"}
+            <label for="row[hobbydata][]-{$key}"><input id="row[hobbydata][]-{$key}" name="row[hobbydata][]" type="checkbox" value="{$key}" {in name="key" value="$row.hobbydata"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="{$row.title|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-content" data-rule="required" class="form-control editor" rows="5" name="row[content]" cols="50">{$row.content|htmlentities}</textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Image')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-image" data-rule="required" class="form-control" size="50" name="row[image]" type="text" value="{$row.image|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-image" class="btn btn-danger plupload" data-input-id="c-image" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-input-id="c-image" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-image"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-image"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-images" data-rule="required" class="form-control" size="50" name="row[images]" type="text" value="{$row.images|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-images" class="btn btn-danger plupload" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-images" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-images"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-images"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Attachfile')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-attachfile" data-rule="required" class="form-control" size="50" name="row[attachfile]" type="text" value="{$row.attachfile|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-attachfile" class="btn btn-danger plupload" data-input-id="c-attachfile" data-multiple="false" data-preview-id="p-attachfile"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-attachfile" class="btn btn-primary fachoose" data-input-id="c-attachfile" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-attachfile"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-attachfile"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Keywords')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-keywords" data-rule="required" class="form-control" name="row[keywords]" type="text" value="{$row.keywords|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Description')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-description" data-rule="required" class="form-control" name="row[description]" type="text" value="{$row.description|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('City')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class='control-relative'><input id="c-city" data-rule="required" class="form-control" data-toggle="city-picker" name="row[city]" type="text" value="{$row.city|htmlentities}"></div>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Json')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <dl class="fieldlist" data-name="row[json]">
+                <dd>
+                    <ins>{:__('Json key')}</ins>
+                    <ins>{:__('Json value')}</ins>
+                </dd>
+                <dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
+                <textarea name="row[json]" class="form-control hide" cols="30" rows="5">{$row.json|htmlentities}</textarea>
+            </dl>
+
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Price')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-price" data-rule="required" class="form-control" step="0.01" name="row[price]" type="number" value="{$row.price|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Views')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-views" data-rule="required" class="form-control" name="row[views]" type="number" value="{$row.views|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Startdate')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-startdate" class="form-control datetimepicker" data-date-format="YYYY-MM-DD" data-use-current="true" name="row[startdate]" type="text" value="{$row.startdate}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Activitytime')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-activitytime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[activitytime]" type="text" value="{$row.activitytime}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Year')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-year" class="form-control datetimepicker" data-date-format="YYYY" data-use-current="true" name="row[year]" type="text" value="{$row.year}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Times')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-times" class="form-control datetimepicker" data-date-format="HH:mm:ss" data-use-current="true" name="row[times]" type="text" value="{$row.times}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Refreshtime')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-refreshtime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[refreshtime]" type="text" value="{:$row.refreshtime?datetime($row.refreshtime):''}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" data-rule="required" class="form-control" name="row[weigh]" type="number" value="{$row.weigh|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Switch')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <input  id="c-switch" name="row[switch]" type="hidden" value="{$row.switch}">
+            <a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-switch" data-yes="1" data-no="0" >
+                <i class="fa fa-toggle-on text-success {eq name="$row.switch" value="0"}fa-flip-horizontal text-gray{/eq} fa-2x"></i>
+            </a>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="statusList" item="vo"}
+            <label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="$row.status"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('State')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="stateList" item="vo"}
+            <label for="row[state]-{$key}"><input id="row[state]-{$key}" name="row[state]" type="radio" value="{$key}" {in name="key" value="$row.state"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 45 - 0
application/admin/view/test/test/index.html

@@ -0,0 +1,45 @@
+<div class="panel panel-default panel-intro">
+    
+    <div class="panel-heading">
+        {:build_heading(null,FALSE)}
+        <ul class="nav nav-tabs" data-field="status">
+            <li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
+            {foreach name="statusList" item="vo"}
+            <li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
+            {/foreach}
+        </ul>
+    </div>
+
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('test/test/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('test/test/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('test/test/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('test/test/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
+
+                        <div class="dropdown btn-group {:$auth->check('test/test/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
+                            </ul>
+                        </div>
+
+                        <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('test/test/recyclebin')?'':'hide'}" href="test/test/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('test/test/edit')}" 
+                           data-operate-del="{:$auth->check('test/test/del')}" 
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 25 - 0
application/admin/view/test/test/recyclebin.html

@@ -0,0 +1,25 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh')}
+                        <a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('test/test/restore')?'':'hide'}" href="javascript:;" data-url="test/test/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
+                        <a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('test/test/destroy')?'':'hide'}" href="javascript:;" data-url="test/test/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
+                        <a class="btn btn-success btn-restoreall {:$auth->check('test/test/restore')?'':'hide'}" href="javascript:;" data-url="test/test/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
+                        <a class="btn btn-danger btn-destroyall {:$auth->check('test/test/destroy')?'':'hide'}" href="javascript:;" data-url="test/test/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover"
+                           data-operate-restore="{:$auth->check('test/test/restore')}"
+                           data-operate-destroy="{:$auth->check('test/test/destroy')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 78 - 0
application/admin/view/users/users/add.html

@@ -0,0 +1,78 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_nickname')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_nickname" data-rule="required" class="form-control" name="row[user_nickname]" type="text">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_tel')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_tel" data-rule="required" class="form-control" name="row[user_tel]" type="text">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_avatar')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-user_avatar" data-rule="required" class="form-control" size="50" name="row[user_avatar]" type="text">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-user_avatar" class="btn btn-danger plupload" data-input-id="c-user_avatar" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-user_avatar"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-user_avatar" class="btn btn-primary fachoose" data-input-id="c-user_avatar" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-user_avatar"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-user_avatar"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_money')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_money" class="form-control" step="0.01" name="row[user_money]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_redbean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_redbean" class="form-control" step="0.01" name="row[user_redbean]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_whitebean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_whitebean" class="form-control" step="0.01" name="row[user_whitebean]" type="number" value="0.00">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_line_num')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_line_num" class="form-control" name="row[user_line_num]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Create_time')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-create_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[create_time]" type="text" value="{:date('Y-m-d H:i:s')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Update_time')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-update_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[update_time]" type="text" value="{:date('Y-m-d H:i:s')}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status_switch')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-status_switch" class="form-control" name="row[status_switch]" type="number" value="1">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 78 - 0
application/admin/view/users/users/edit.html

@@ -0,0 +1,78 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_nickname')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_nickname" data-rule="required" class="form-control" name="row[user_nickname]" type="text" value="{$row.user_nickname|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_tel')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_tel" data-rule="required" class="form-control" name="row[user_tel]" type="text" value="{$row.user_tel|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_avatar')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-user_avatar" data-rule="required" class="form-control" size="50" name="row[user_avatar]" type="text" value="{$row.user_avatar|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-user_avatar" class="btn btn-danger plupload" data-input-id="c-user_avatar" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-user_avatar"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                    <span><button type="button" id="fachoose-user_avatar" class="btn btn-primary fachoose" data-input-id="c-user_avatar" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-user_avatar"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-user_avatar"></ul>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_money')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_money" class="form-control" step="0.01" name="row[user_money]" type="number" value="{$row.user_money|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_redbean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_redbean" class="form-control" step="0.01" name="row[user_redbean]" type="number" value="{$row.user_redbean|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_whitebean')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_whitebean" class="form-control" step="0.01" name="row[user_whitebean]" type="number" value="{$row.user_whitebean|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('User_line_num')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-user_line_num" class="form-control" name="row[user_line_num]" type="number" value="{$row.user_line_num|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Create_time')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-create_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[create_time]" type="text" value="{:$row.create_time?datetime($row.create_time):''}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Update_time')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-update_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[update_time]" type="text" value="{:$row.update_time?datetime($row.update_time):''}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status_switch')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-status_switch" class="form-control" name="row[status_switch]" type="number" value="{$row.status_switch|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 35 - 0
application/admin/view/users/users/index.html

@@ -0,0 +1,35 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+<!--                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('users/users/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>-->
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('users/users/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('users/users/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('users/users/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
+
+                        <div class="dropdown btn-group {:$auth->check('users/users/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
+                            </ul>
+                        </div>
+
+                        
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('users/users/edit')}" 
+                           data-operate-del="{:$auth->check('users/users/del')}" 
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 86 - 0
public/assets/js/backend/commodity/commodity.js

@@ -0,0 +1,86 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'commodity/commodity/index' + location.search,
+                    add_url: 'commodity/commodity/add',
+                    edit_url: 'commodity/commodity/edit',
+                    del_url: 'commodity/commodity/del',
+                    multi_url: 'commodity/commodity/multi',
+                    table: 'commodity',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'c_id',
+                sortName: 'c_id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'c_id', title: __('C_id')},
+                        {field: 'c_name', title: __('C_name')},
+                        {field: 'c_images', title: __('C_images'), events: Table.api.events.image, formatter: Table.api.formatter.images},
+                        {
+                            field: 'buttons',
+                            width: "120px",
+                            title: __('点击查看商品配置'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'detail',
+                                    text: __('点击修改商品配置'),
+                                    title: __('购买详情'),
+                                    classname: 'btn btn-xs btn-primary btn-dialog',
+                                    icon: 'fa fa-list',
+                                    url: "commodity/commodity/commodityInfo",
+                                    callback: function (data) {
+                                        Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
+                                    },
+                                    visible: function (row) {
+                                        //返回true时按钮显示,返回false隐藏
+                                        return true;
+                                    }
+                                },
+                            ],
+                            formatter: Table.api.formatter.buttons
+                        },
+                        {field: 'c_yuanjiaprice', title: __('C_yuanjiaprice'), operate:'BETWEEN'},
+                        {field: 'c_vipprice', title: __('C_vipprice'), operate:'BETWEEN'},
+                        {field: 'c_freight', title: __('C_freight'), operate:'BETWEEN'},
+                        {field: 'c_buynum', title: __('C_buynum')},
+                        {field: 'c_stock', title: __('C_stock')},
+                        {field: 'c_type', title: __('C_type')},
+                        {field: 'c_whitebean', title: __('C_whitebean')},
+                        {field: 'c_sort', title: __('C_sort')},
+                        {field: 'c_recommodity', title: __('是否首页推荐'), searchList: {"1":__('Yes'),"0":__('No')}, formatter: Table.api.formatter.toggle},
+                        {field: 'c_state_switch', title: __('C_state_switch'), searchList: {"1":__('Yes'),"0":__('No')}, formatter: Table.api.formatter.toggle},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 50 - 0
public/assets/js/backend/ctype/ctype.js

@@ -0,0 +1,50 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'ctype/ctype/index' + location.search,
+                    add_url: 'ctype/ctype/add',
+                    edit_url: 'ctype/ctype/edit',
+                    del_url: 'ctype/ctype/del',
+                    multi_url: 'ctype/ctype/multi',
+                    table: 'ctype',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 't_id',
+                sortName: 't_id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 't_id', title: __('T_id')},
+                        {field: 't_name', title: __('T_name')},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 136 - 0
public/assets/js/backend/test/test.js

@@ -0,0 +1,136 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'test/test/index' + location.search,
+                    add_url: 'test/test/add',
+                    edit_url: 'test/test/edit',
+                    del_url: 'test/test/del',
+                    multi_url: 'test/test/multi',
+                    table: 'test',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'weigh',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'admin_id', title: __('Admin_id')},
+                        {field: 'category_id', title: __('Category_id')},
+                        {field: 'category_ids', title: __('Category_ids')},
+                        {field: 'week', title: __('Week'), searchList: {"monday":__('Week monday'),"tuesday":__('Week tuesday'),"wednesday":__('Week wednesday')}, formatter: Table.api.formatter.normal},
+                        {field: 'flag', title: __('Flag'), searchList: {"hot":__('Flag hot'),"index":__('Flag index'),"recommend":__('Flag recommend')}, operate:'FIND_IN_SET', formatter: Table.api.formatter.label},
+                        {field: 'genderdata', title: __('Genderdata'), searchList: {"male":__('Genderdata male'),"female":__('Genderdata female')}, formatter: Table.api.formatter.normal},
+                        {field: 'hobbydata', title: __('Hobbydata'), searchList: {"music":__('Hobbydata music'),"reading":__('Hobbydata reading'),"swimming":__('Hobbydata swimming')}, operate:'FIND_IN_SET', formatter: Table.api.formatter.label},
+                        {field: 'title', title: __('Title')},
+                        {field: 'image', title: __('Image'), events: Table.api.events.image, formatter: Table.api.formatter.image},
+                        {field: 'images', title: __('Images'), events: Table.api.events.image, formatter: Table.api.formatter.images},
+                        {field: 'attachfile', title: __('Attachfile')},
+                        {field: 'keywords', title: __('Keywords')},
+                        {field: 'description', title: __('Description')},
+                        {field: 'city', title: __('City')},
+                        {field: 'price', title: __('Price'), operate:'BETWEEN'},
+                        {field: 'views', title: __('Views')},
+                        {field: 'startdate', title: __('Startdate'), operate:'RANGE', addclass:'datetimerange'},
+                        {field: 'activitytime', title: __('Activitytime'), operate:'RANGE', addclass:'datetimerange'},
+                        {field: 'year', title: __('Year')},
+                        {field: 'times', title: __('Times')},
+                        {field: 'refreshtime', title: __('Refreshtime'), operate:'RANGE', addclass:'datetimerange', formatter: Table.api.formatter.datetime},
+                        {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate:'RANGE', addclass:'datetimerange', formatter: Table.api.formatter.datetime},
+                        {field: 'weigh', title: __('Weigh')},
+                        {field: 'switch', title: __('Switch'), searchList: {"1":__('Yes'),"0":__('No')}, formatter: Table.api.formatter.toggle},
+                        {field: 'status', title: __('Status'), searchList: {"normal":__('Normal'),"hidden":__('Hidden')}, formatter: Table.api.formatter.status},
+                        {field: 'state', title: __('State'), searchList: {"0":__('State 0'),"1":__('State 1'),"2":__('State 2')}, formatter: Table.api.formatter.normal},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        recyclebin: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    'dragsort_url': ''
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: 'test/test/recyclebin' + location.search,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'title', title: __('Title'), align: 'left'},
+                        {
+                            field: 'deletetime',
+                            title: __('Deletetime'),
+                            operate: 'RANGE',
+                            addclass: 'datetimerange',
+                            formatter: Table.api.formatter.datetime
+                        },
+                        {
+                            field: 'operate',
+                            width: '130px',
+                            title: __('Operate'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'Restore',
+                                    text: __('Restore'),
+                                    classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
+                                    icon: 'fa fa-rotate-left',
+                                    url: 'test/test/restore',
+                                    refresh: true
+                                },
+                                {
+                                    name: 'Destroy',
+                                    text: __('Destroy'),
+                                    classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
+                                    icon: 'fa fa-times',
+                                    url: 'test/test/destroy',
+                                    refresh: true
+                                }
+                            ],
+                            formatter: Table.api.formatter.operate
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 60 - 0
public/assets/js/backend/users/users.js

@@ -0,0 +1,60 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'users/users/index' + location.search,
+                    add_url: 'users/users/add',
+                    edit_url: 'users/users/edit',
+                    del_url: 'users/users/del',
+                    multi_url: 'users/users/multi',
+                    table: 'users',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'user_id',
+                sortName: 'user_id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'user_id', title: __('User_id')},
+                        {field: 'user_nickname', title: __('User_nickname')},
+                        {field: 'user_tel', title: __('User_tel')},
+                        {field: 'user_avatar', title: __('User_avatar'), events: Table.api.events.image, formatter: Table.api.formatter.image},
+                        {field: 'user_level', title: __('等级'),operate: false, formatter:Table.api.formatter.status, searchList:{1: '普通用户', 2: 'VIP', 3: '铂金会员', 4: '钻石会员'}},
+                        {field: 'user_money', title: __('User_money'), operate:'BETWEEN'},
+                        {field: 'user_redbean', title: __('User_redbean'), operate:'BETWEEN'},
+                        {field: 'user_whitebean', title: __('User_whitebean'), operate:'BETWEEN'},
+                        {field: 'user_line_num', title: __('User_line_num')},
+                        {field: 'create_time', title: __('Create_time'), operate:'RANGE', addclass:'create_time', formatter: Table.api.formatter.datetime},
+                        {field: 'update_time', title: __('Update_time'), operate:'RANGE', addclass:'update_time', formatter: Table.api.formatter.datetime},
+                        {field: 'status_switch', title: __('状态'), searchList: {1:__('启用'),0:__('禁用')}, formatter: Table.api.formatter.toggle},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 2 - 1
public/assets/js/require-table.js

@@ -514,6 +514,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     return html;
                 },
                 toggle: function (value, row, index) {
+                    var options = $(table).bootstrapTable('getOptions'); //新增这行
                     var color = typeof this.color !== 'undefined' ? this.color : 'success';
                     var yes = typeof this.yes !== 'undefined' ? this.yes : 1;
                     var no = typeof this.no !== 'undefined' ? this.no : 0;
@@ -523,7 +524,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                         disable = typeof this.disable === "function" ? this.disable.call(this, value, row, index) : this.disable;
                     }
                     return "<a href='javascript:;' data-toggle='tooltip' title='" + __('Click to toggle') + "' class='btn-change " + (disable ? 'btn disabled' : '') + "' data-id='"
-                        + row.id + "' " + (url ? "data-url='" + url + "'" : "") + " data-params='" + this.field + "=" + (value == yes ? no : yes) + "'><i class='fa fa-toggle-on " + (value == yes ? 'text-' + color : 'fa-flip-horizontal text-gray') + " fa-2x'></i></a>";
+                        + row[options.pk] + "' " + (url ? "data-url='" + url + "'" : "") + " data-params='" + this.field + "=" + (value == yes ? no : yes) + "'><i class='fa fa-toggle-on " + (value == yes ? 'text-' + color : 'fa-flip-horizontal text-gray') + " fa-2x'></i></a>";
                 },
                 url: function (value, row, index) {
                     return '<div class="input-group input-group-sm" style="width:250px;margin:0 auto;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';

+ 1 - 0
thinkphp/library/think/Model.php

@@ -995,6 +995,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
         } elseif (empty($this->pk)) {
             $this->pk = $this->getQuery()->getPk();
         }
+       // halt($this->pk);
         return $this->pk;
     }
 

部分文件因文件數量過多而無法顯示