xxxrrrdddd 2 years ago
parent
commit
297756289a
44 changed files with 36 additions and 13345 deletions
  1. 0 1
      addons/alioss/.addonrc
  2. 0 144
      addons/alioss/Alioss.php
  3. 0 0
      addons/alioss/assets/js/spark.js
  4. 0 82
      addons/alioss/bootstrap.js
  5. 0 151
      addons/alioss/config.php
  6. 0 69
      addons/alioss/controller/Index.php
  7. 0 8
      addons/alioss/info.ini
  8. 0 1
      addons/alioss/install.sql
  9. 0 78
      addons/alioss/library/Auth.php
  10. 0 1
      addons/qcloudsms/.addonrc
  11. 0 197
      addons/qcloudsms/Qcloudsms.php
  12. 0 128
      addons/qcloudsms/config.php
  13. 0 15
      addons/qcloudsms/controller/Index.php
  14. 0 10
      addons/qcloudsms/info.ini
  15. 0 69
      addons/qcloudsms/library/FileVoiceSender.php
  16. 0 91
      addons/qcloudsms/library/SmsMobileStatusPuller.php
  17. 0 99
      addons/qcloudsms/library/SmsMultiSender.php
  18. 0 211
      addons/qcloudsms/library/SmsSenderUtil.php
  19. 0 107
      addons/qcloudsms/library/SmsSingleSender.php
  20. 0 75
      addons/qcloudsms/library/SmsStatusPuller.php
  21. 0 71
      addons/qcloudsms/library/SmsVoicePromptSender.php
  22. 0 67
      addons/qcloudsms/library/SmsVoiceVerifyCodeSender.php
  23. 0 77
      addons/qcloudsms/library/TtsVoiceSender.php
  24. 0 1
      addons/simditor/.addonrc
  25. 0 31
      addons/simditor/Simditor.php
  26. 0 0
      addons/simditor/assets/css/simditor.min.css
  27. BIN
      addons/simditor/assets/images/image.png
  28. 0 0
      addons/simditor/assets/js/simditor.min.js
  29. 0 48
      addons/simditor/bootstrap.js
  30. 0 4
      addons/simditor/build/build.sh
  31. 0 4
      addons/simditor/build/css.js
  32. 0 10
      addons/simditor/build/js.js
  33. 0 4699
      addons/simditor/build/r.js
  34. 0 4
      addons/simditor/config.php
  35. 0 10
      addons/simditor/info.ini
  36. 0 460
      addons/simditor/src/css/mobile.css
  37. 0 2
      addons/simditor/src/css/simditor.css
  38. BIN
      addons/simditor/src/images/image.png
  39. 0 241
      addons/simditor/src/js/hotkeys.js
  40. 0 172
      addons/simditor/src/js/module.js
  41. 0 5641
      addons/simditor/src/js/simditor.js
  42. 0 261
      addons/simditor/src/js/uploader.js
  43. 29 0
      application/admin/controller/Ad.php
  44. 7 5
      application/common/model/Ad.php

+ 0 - 1
addons/alioss/.addonrc

@@ -1 +0,0 @@
-{"files":["public\\assets\\addons\\alioss\\js\\spark.js"]}

+ 0 - 144
addons/alioss/Alioss.php

@@ -1,144 +0,0 @@
-<?php
-
-namespace addons\alioss;
-
-use app\common\model\Attachment;
-use think\Addons;
-
-/**
- * 阿里云OSS上传插件
- */
-class Alioss extends Addons
-{
-
-    /**
-     * 插件安装方法
-     * @return bool
-     */
-    public function install()
-    {
-        return true;
-    }
-
-    /**
-     * 插件卸载方法
-     * @return bool
-     */
-    public function uninstall()
-    {
-        return true;
-    }
-
-    /**
-     * 加载配置
-     */
-    public function uploadConfigInit(&$upload)
-    {
-        $config = $this->getConfig();
-        if ($config['uploadmode'] === 'client')
-        {
-            $upload = [
-                'cdnurl'    => $config['cdnurl'],
-                'uploadurl' => 'http://' . $config['bucket'] . '.' . $config['endpoint'],
-                'bucket'    => $config['bucket'],
-                'maxsize'   => $config['maxsize'],
-                'mimetype'  => $config['mimetype'],
-                'multipart' => [],
-                'multiple'  => $config['multiple'] ? true : false,
-                'storage'   => 'alioss',
-                'chunking'  => (bool)($config['chunking']??false)
-            ];
-        }
-        else
-        {
-            $upload = array_merge($upload, [
-                'maxsize'  => $config['maxsize'],
-                'mimetype' => $config['mimetype'],
-                'multiple' => $config['multiple'] ? true : false,
-            ]);
-        }
-    }
-
-    /**
-     * 上传成功后
-     */
-    public function uploadAfter(Attachment $attachment)
-    {
-        $config = $this->getConfig();
-        if ($config['uploadmode']/* === 'server'*/)
-        {
-            $file = ROOT_PATH . 'public' . str_replace('/', DIRECTORY_SEPARATOR, parse_url($attachment->url,PHP_URL_PATH));
-
-            $name = basename($file);
-            $md5 = md5_file($file);
-
-            $auth = new \addons\alioss\library\Auth();
-            $params = $auth->params($name, $md5, false);
-            $multipart = [
-                [
-                    'name'     => 'key',
-                    'contents' => $params['key'],
-                ],
-                [
-                    'name'     => 'success_action_status',
-                    'contents' => 200,
-                ],
-                [
-                    'name'     => 'OSSAccessKeyId',
-                    'contents' => $params['id'],
-                ],
-                [
-                    'name'     => 'policy',
-                    'contents' => $params['policy'],
-                ],
-                [
-                    'name'     => 'Signature',
-                    'contents' => $params['signature'],
-                ],
-                [
-                    'name'     => 'file',
-                    'contents' => fopen($file, 'r'),
-                ],
-            ];
-            try
-            {
-                $uploadurl = 'http://' . $config['bucket'] . '.' . $config['endpoint'];
-
-                $client = new \GuzzleHttp\Client();
-//                $res = $client->request('POST', $uploadurl, [
-//                    'multipart' => $multipart,
-//                    'headers'   => ['Accept-Encoding' => 'gzip'],
-//                ]);
-                
-                $multipartStream = new \GuzzleHttp\Psr7\MultipartStream($multipart);
-                $boundary = $multipartStream->getBoundary();
-                $body = (string) $multipartStream;
-                //默认的request方法会添加Content-Length字段,但Alioss不识别,所以需要移除
-                $body = preg_replace('/Content\-Length:\s(\d+)[\r\n]+Content\-Type/i', "Content-Type", $body);
-                $params = [
-                    'headers' => [
-                        'Connection'   => 'close',
-                        'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
-                    ],
-                    'body'    => $body,
-                ];
-
-                $res = $client->request('POST', $uploadurl, $params);
-                $code = $res->getStatusCode();
-                //成功不做任何操作
-                if($config['uploadmode']=='client'){
-                    if($attachment->id) {
-                        $attachment->delete();
-                    }
-                    unlink($file);
-                }
-            }
-            catch (\GuzzleHttp\Exception\ClientException $e)
-            {
-                echo json_encode(['code' => 0, 'msg' => '无法上传到远程服务器,错误:' . $e->getMessage()]);
-                exit;
-            }
-        }
-    }
-
-}

File diff suppressed because it is too large
+ 0 - 0
addons/alioss/assets/js/spark.js


+ 0 - 82
addons/alioss/bootstrap.js

@@ -1,82 +0,0 @@
-//如果开启了alioss客户端上传模式
-if (typeof Config.upload.storage !== 'undefined' && Config.upload.storage === 'alioss') {
-    require(['upload', '../addons/alioss/js/spark'], function (Upload, SparkMD5) {
-        var _onFileAdded = Upload.events.onFileAdded;
-        var _onUploadResponse = Upload.events.onUploadResponse;
-        var _process = function (up, file) {
-            (function (up, file) {
-                var blob = file.getNative();
-                var loadedBytes = file.loaded;
-                var chunkSize = 2097152;
-                var chunkBlob = blob.slice(loadedBytes, loadedBytes + chunkSize);
-                var reader = new FileReader();
-                reader.addEventListener('loadend', function (e) {
-                    var spark = new SparkMD5.ArrayBuffer();
-                    spark.append(e.target.result);
-                    var md5 = spark.end();
-                    Fast.api.ajax({
-                        url: "/addons/alioss/index/params",
-                        data: {method: 'POST', md5: md5, name: file.name, type: file.type, size: file.size},
-                    }, function (data) {
-                        file.md5 = md5;
-                        file.status = 1;
-                        file.key = data.key;
-                        file.OSSAccessKeyId = data.id;
-                        file.policy = data.policy;
-                        file.signature = data.signature;
-                        up.start();
-                        return false;
-                    });
-                    return;
-                });
-                reader.readAsArrayBuffer(chunkBlob);
-            })(up, file);
-        };
-        Upload.events.onFileAdded = function (up, files) {
-            return _onFileAdded.call(this, up, files);
-        };
-        Upload.events.onBeforeUpload = function (up, file) {
-            if (typeof file.md5 === 'undefined') {
-                up.stop();
-                _process(up, file);
-            } else {
-                up.settings.headers = up.settings.headers || {};
-                up.settings.multipart_params.key = file.key;
-                up.settings.multipart_params.OSSAccessKeyId = file.OSSAccessKeyId;
-                up.settings.multipart_params.success_action_status = 200;
-                if (typeof file.callback !== 'undefined') {
-                    up.settings.multipart_params.callback = file.callback;
-                }
-                up.settings.multipart_params.policy = file.policy;
-                up.settings.multipart_params.signature = file.signature;
-                //up.settings.send_file_name = false;
-            }
-        };
-        Upload.events.onUploadResponse = function (response, info, up, file) {
-            try {
-                var ret = {};
-                if (info.status === 200) {
-                    var url = '/' + file.key;
-                    Fast.api.ajax({
-                        url: "/addons/alioss/index/notify",
-                        data: {method: 'POST', name: file.name, url: url, md5: file.md5, size: file.size, type: file.type, policy: file.policy, signature: file.signature}
-                    }, function () {
-                        return false;
-                    });
-                    ret.code = 1;
-                    ret.data = {
-                        url: url
-                    };
-                } else {
-                    ret.code = 0;
-                    ret.msg = info.response;
-                }
-                return _onUploadResponse.call(this, JSON.stringify(ret));
-
-            } catch (e) {
-            }
-            return _onUploadResponse.call(this, response);
-
-        };
-    });
-}

+ 0 - 151
addons/alioss/config.php

@@ -1,151 +0,0 @@
-<?php
-
-return [
-    [
-        'name' => 'app_id',
-        'title' => 'app_id',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'asdasd',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'app_key',
-        'title' => 'app_key',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'asdasda',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'bucket',
-        'title' => 'Bucket',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'dasdasda',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '阿里云OSS的空间名',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'endpoint',
-        'title' => 'EndPoint',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'oss-cn-shenzhen.aliyuncs.com',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '如果是服务器中转模式,可填写内网域名',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'cdnurl',
-        'title' => 'CDN地址',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'http://yourbucket.oss-cn-shenzhen.aliyuncs.com',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '如果你启用了CDN,请填写CDN地址',
-        'ok' => '',
-        'extend' => '',
-    ],
-    6 => [
-        'name' => 'uploadmode',
-        'title' => '上传模式',
-        'type' => 'select',
-        'content' => [
-            'client' => '客户端直传(速度快,无备份)',
-            'server' => '服务器中转(占用服务器带宽,有备份)',
-        ],
-        'value' => 'client',
-        'rule' => '',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'savekey',
-        'title' => '保存文件名',
-        'type' => 'string',
-        'content' => [],
-        'value' => '/uploads/{year}{mon}{day}/{filemd5}{.suffix}',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'expire',
-        'title' => '上传有效时长',
-        'type' => 'string',
-        'content' => [],
-        'value' => '600',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'maxsize',
-        'title' => '最大可上传',
-        'type' => 'string',
-        'content' => [],
-        'value' => '10M',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'mimetype',
-        'title' => '可上传后缀格式',
-        'type' => 'string',
-        'content' => [],
-        'value' => '*',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'multiple',
-        'title' => '多文件上传',
-        'type' => 'bool',
-        'content' => [],
-        'value' => '0',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'chunking',
-        'title' => '分片上传',
-        'type' => 'bool',
-        'content' => [],
-        'value' => '0',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-];

+ 0 - 69
addons/alioss/controller/Index.php

@@ -1,69 +0,0 @@
-<?php
-
-namespace addons\alioss\controller;
-
-use app\common\model\Attachment;
-use think\addons\Controller;
-
-/**
- * Ucloud
- *
- */
-class Index extends Controller
-{
-
-    public function index()
-    {
-        $this->error("当前插件暂无前台页面");
-    }
-
-    public function params()
-    {
-        $name = $this->request->post('name');
-        $md5 = $this->request->post('md5');
-        $auth = new \addons\alioss\library\Auth();
-        $params = $auth->params($name, $md5);
-        $this->success('', null, $params);
-        return;
-    }
-
-    public function notify()
-    {
-        $size = $this->request->post('size');
-        $name = $this->request->post('name');
-        $md5 = $this->request->post('md5');
-        $type = $this->request->post('type');
-        $signature = $this->request->post('signature');
-        $policy = $this->request->post('policy');
-        $url = $this->request->post('url');
-        $suffix = substr($name, stripos($name, '.') + 1);
-        $auth = new \addons\alioss\library\Auth();
-        if ($auth->check($signature, $policy))
-        {
-            $attachment = Attachment::getBySha1($md5);
-            if (!$attachment)
-            {
-                $params = array(
-                    'filesize'    => $size,
-                    'imagewidth'  => 0,
-                    'imageheight' => 0,
-                    'imagetype'   => $suffix,
-                    'imageframes' => 0,
-                    'mimetype'    => $type,
-                    'url'         => $url,
-                    'uploadtime'  => time(),
-                    'storage'     => 'alioss',
-                    'sha1'        => $md5,
-                );
-                Attachment::create($params);
-            }
-            $this->success();
-        }
-        else
-        {
-            $this->error(__('You have no permission'));
-        }
-        return;
-    }
-
-}

+ 0 - 8
addons/alioss/info.ini

@@ -1,8 +0,0 @@
-name = alioss
-title = 阿里OSS上传
-intro = 使用阿里OSS存储,上传时直传阿里云OSS
-author = Karson
-website = http://www.fastadmin.net
-version = 1.1.7
-state = 0
-url = /addons/alioss

+ 0 - 1
addons/alioss/install.sql

@@ -1 +0,0 @@
-ALTER TABLE `__PREFIX__attachment` MODIFY COLUMN `storage` enum('local','upyun','qiniu','ucloud','alioss') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'local' COMMENT '存储位置' AFTER `uploadtime`;

+ 0 - 78
addons/alioss/library/Auth.php

@@ -1,78 +0,0 @@
-<?php
-
-namespace addons\alioss\library;
-
-class Auth
-{
-
-    public function __construct()
-    {
-        
-    }
-
-    public function params($name, $md5, $callback = true)
-    {
-        $config = get_addon_config('alioss');
-        $callback_param = array(
-            'callbackUrl'      => isset($config['notifyurl']) ? $config['notifyurl'] : '',
-            'callbackBody'     => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
-            'callbackBodyType' => "application/x-www-form-urlencoded"
-        );
-
-        $base64_callback_body = base64_encode(json_encode($callback_param));
-
-        $now = time();
-        $end = $now + $config['expire']; //设置该policy超时时间是10s. 即这个policy过了这个有效时间,将不能访问
-        $expiration = $this->gmt_iso8601($end);
-
-        preg_match('/(\d+)(\w+)/', $config['maxsize'], $matches);
-        $type = strtolower($matches[2]);
-        $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
-        $size = (int) $config['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
-
-        //最大文件大小.用户可以自己设置
-        $condition = array(0 => 'content-length-range', 1 => 0, 2 => $size);
-        $conditions[] = $condition;
-
-        //表示用户上传的数据,必须是以$dir开始, 不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录
-        //$start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
-        //$conditions[] = $start;
-
-        $arr = array('expiration' => $expiration, 'conditions' => $conditions);
-
-        $policy = base64_encode(json_encode($arr));
-        $signature = base64_encode(hash_hmac('sha1', $policy, $config['app_key'], true));
-
-        $suffix = substr($name, stripos($name, '.') + 1);
-        $search = ['{year}', '{mon}', '{month}', '{day}', '{filemd5}', '{suffix}', '{.suffix}'];
-        $replace = [date("Y"), date("m"), date("m"), date("d"), $md5, $suffix, '.' . $suffix];
-        $key = ltrim(str_replace($search, $replace, $config['savekey']), '/');
-
-        $response = array();
-        $response['id'] = $config['app_id'];
-        $response['key'] = $key;
-        $response['policy'] = $policy;
-        $response['signature'] = $signature;
-        $response['expire'] = $end;
-        $response['callback'] = '';
-        return $response;
-    }
-
-    public function check($signature, $policy)
-    {
-        $config = get_addon_config('alioss');
-        $sign = base64_encode(hash_hmac('sha1', $policy, $config['app_key'], true));
-        return $signature == $sign;
-    }
-
-    private function gmt_iso8601($time)
-    {
-        $dtStr = date("c", $time);
-        $mydatetime = new \DateTime($dtStr);
-        $expiration = $mydatetime->format(\DateTime::ISO8601);
-        $pos = strpos($expiration, '+');
-        $expiration = substr($expiration, 0, $pos);
-        return $expiration . "Z";
-    }
-
-}

+ 0 - 1
addons/qcloudsms/.addonrc

@@ -1 +0,0 @@
-{"license":"regular","licenseto":"39487","licensekey":"5WZLFIqPHk1nj4eV JH\/3+YtThUj0D3m9ql7miJOU5jsrrPnvHW0bIVnEydE="}

+ 0 - 197
addons/qcloudsms/Qcloudsms.php

@@ -1,197 +0,0 @@
-<?php
-
-namespace addons\qcloudsms;
-
-use addons\qcloudsms\library\SmsSingleSender;
-use addons\qcloudsms\library\SmsVoicePromptSender;
-use addons\qcloudsms\library\SmsVoiceverifyCodeSender;
-use addons\qcloudsms\library\TtsVoiceSender;
-use think\Addons;
-use think\Config;
-
-/**
- * 插件
- */
-class Qcloudsms extends Addons
-{
-    private $appid = null;
-    private $appkey = null;
-    private $config = null;
-    private $sender = null;
-    private $sendError = '';
-
-    public function ConfigInit()
-    {
-        $this->config = $this->getConfig();
-        //如果使用语音短信  更换成语音短信模板
-        if ($this->config['isVoice'] == 1) {
-            $this->config['template'] = $this->config['voiceTemplate'];
-            //语音短信 需要另行设置Aappid 与Appkey
-            $this->appid = $this->config['voiceAppid'];
-            $this->appkey = $this->config['voiceAppkey'];
-        } else {
-            $this->appid = $this->config['appid'];
-            $this->appkey = $this->config['appkey'];
-        }
-    }
-
-    /**
-     * 短信发送行为
-     * @param Sms $params
-     * @return  boolean
-     */
-    public function smsSend(&$params)
-    {
-        $this->ConfigInit();
-        try {
-            if ($this->config['isTemplateSender'] == 1) {
-                $templateID = $this->config['template'][$params->event];
-                if ($this->config['isVoice'] != 1) {
-                    //普通短信发送
-                    $this->sender = new SmsSingleSender($this->appid, $this->appkey);
-                    $result = $this->sender->sendWithParam("86", $params['mobile'], $templateID, ["{$params->code}"], $this->config['sign'], "", "");
-                } else {
-                    //语音短信发送
-                    $this->sender = new TtsVoiceSender($this->appid, $this->appkey);
-                    //参数: 国家码,手机号、模板ID、模板参数、播放次数(可选字段)、用户的session内容,服务器端原样返回(可选字段)
-                    $result = $this->sender->send("86", $params['mobile'], $templateID, [$params->code]);
-                }
-            } else {
-                //判断是否是语音短信
-                if ($this->config['isVoice'] != 1) {
-                    $this->sender = new SmsSingleSender($this->appid, $this->appkey);
-                    //参数:短信类型{1营销短信,0普通短信 }、国家码、手机号、短信内容、扩展码(可留空)、服务的原样返回的参数
-                    $result = $this->sender->send($params['type'], '86', $params['mobile'], $params['msg'], "", "");
-                } else {
-                    $this->sender = new SmsVoiceVerifyCodeSender($this->appid, $this->appkey);
-                    //参数:国家码、手机号、短信内容、播放次数(默认2次)、服务的原样返回的参数
-                    $result = $this->sender->send('86', $params['mobile'], $params['msg']);
-                }
-            }
-
-            $rsp = json_decode($result, true);
-            if ($rsp['result'] == 0 && $rsp['errmsg'] == 'OK') {
-                return true;
-            } else {
-                //记录错误信息
-                $this->setError($rsp);
-                return false;
-            }
-        } catch (\Exception $e) {
-            $this->setError($e->getMessage());
-        }
-        return false;
-    }
-
-    /**
-     * 短信发送通知
-     * @param array $params
-     * @return  boolean
-     */
-    public function smsNotice(&$params)
-    {
-        $this->ConfigInit();
-        try {
-            if ($this->config['isTemplateSender'] == 1) {
-                $templateID = $this->config['template'][$params['template']];
-
-                if ($this->config['isVoice'] != 1) {
-                    //普通短信发送
-                    $this->sender = new SmsSingleSender($this->appid, $this->appkey);
-                    $result = $this->sender->sendWithParam("86", $params['mobile'], $templateID, ["{$params['msg']}"], $this->config['sign'], "", "");
-                } else {
-                    //语音短信发送
-                    $this->sender = new TtsVoiceSender($this->appid, $this->appkey);
-                    //参数: 国家码,手机号、模板ID、模板参数、播放次数(可选字段)、用户的session内容,服务器端原样返回(可选字段)
-                    $result = $this->sender->send("86", $params['mobile'], $templateID, [$params['msg']]);
-                }
-            } else {
-                //判断是否是语音短信
-                if ($this->config['isVoice'] != 1) {
-                    $this->sender = new SmsSingleSender($this->appid, $this->appkey);
-                    //参数:短信类型{1营销短信,0普通短信 }、国家码、手机号、短信内容、扩展码(可留空)、服务的原样返回的参数
-                    $result = $this->sender->send($params['type'], '86', $params['mobile'], $params['msg'], "", "");
-                } else {
-                    $this->sender = new SmsVoicePromptSender($this->appid, $this->appkey);
-                    //参数:国家码、手机号、语音类型(目前固定为2)、短信内容、播放次数(默认2次)、服务的原样返回的参数
-                    $result = $this->sender->send('86', $params['mobile'], 2, $params['msg']);
-                }
-            }
-            $rsp = (array)json_decode($result, true);
-            if ($rsp['result'] == 0 && $rsp['errmsg'] == 'OK') {
-                return true;
-            } else {
-                //记录错误信息
-                $this->setError($rsp);
-                return false;
-            }
-        } catch (\Exception $e) {
-            var_dump($e);
-            exit();
-        }
-    }
-
-    /**
-     * 记录失败信息
-     * @param [type] $err [description]
-     */
-    private function setError($err)
-    {
-        $this->sendError = $err;
-    }
-
-    /**
-     * 获取失败信息
-     * @return [type] [description]
-     */
-    public function getError()
-    {
-        return $this->sendError;
-    }
-
-    /**
-     * 检测验证是否正确
-     * @param Sms $params
-     * @return  boolean
-     */
-    public function smsCheck(&$params)
-    {
-        return true;
-    }
-
-    /**
-     * 插件安装方法
-     * @return bool
-     */
-    public function install()
-    {
-        return true;
-    }
-
-    /**
-     * 插件卸载方法
-     * @return bool
-     */
-    public function uninstall()
-    {
-        return true;
-    }
-
-    /**
-     * 插件启用方法
-     * @return bool
-     */
-    public function enable()
-    {
-        return true;
-    }
-
-    /**
-     * 插件禁用方法
-     * @return bool
-     */
-    public function disable()
-    {
-        return true;
-    }
-}

+ 0 - 128
addons/qcloudsms/config.php

@@ -1,128 +0,0 @@
-<?php
-
-return [
-    [
-        'name' => 'appid',
-        'title' => '应用AppID',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'asdas',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'appkey',
-        'title' => '应用AppKEY',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'dasdasd',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'voiceAppid',
-        'title' => '语音短信AppID',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'asdasd',
-        'rule' => 'required',
-        'msg' => '使用语音短信必须设置',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'voiceAppkey',
-        'title' => '语音短信AppKEY',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'asdasd',
-        'rule' => 'required',
-        'msg' => '使用语音短信必须设置',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'sign',
-        'title' => '签名',
-        'type' => 'string',
-        'content' => [],
-        'value' => 'your sign',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'isVoice',
-        'title' => '是否使用语音短信',
-        'type' => 'radio',
-        'content' => [
-            '否',
-            '是',
-        ],
-        'value' => '0',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'isTemplateSender',
-        'title' => '是否使用短信模板发送',
-        'type' => 'radio',
-        'content' => [
-            '否',
-            '是',
-        ],
-        'value' => '1',
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'template',
-        'title' => '短信模板',
-        'type' => 'array',
-        'content' => [],
-        'value' => [
-            'register' => '',
-            'resetpwd' => '',
-            'changepwd' => '',
-            'profile' => '',
-        ],
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-    [
-        'name' => 'voiceTemplate',
-        'title' => '语音短信模板',
-        'type' => 'array',
-        'content' => [],
-        'value' => [
-            'register' => '',
-            'resetpwd' => '',
-            'changepwd' => '',
-            'profile' => '',
-        ],
-        'rule' => 'required',
-        'msg' => '',
-        'tip' => '',
-        'ok' => '',
-        'extend' => '',
-    ],
-];

+ 0 - 15
addons/qcloudsms/controller/Index.php

@@ -1,15 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\controller;
-
-use think\addons\Controller;
-
-class Index extends Controller
-{
-
-    public function index()
-    {
-        $this->error("当前插件暂无前台页面");
-    }
-
-}

+ 0 - 10
addons/qcloudsms/info.ini

@@ -1,10 +0,0 @@
-name = qcloudsms
-title = 腾讯云短信发送插件
-intro = 腾讯云短信发送插件
-author = Seacent
-website = https://www.seacent.com
-version = 1.0.3
-state = 0
-url = /addons/qcloudsms
-license = regular
-licenseto = 39487

+ 0 - 69
addons/qcloudsms/library/FileVoiceSender.php

@@ -1,69 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-
-/**
- * 按语音文件fid发送语音通知类
- *
- */
-class FileVoiceSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendfvoice";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     *
-     * 按语音文件fid发送语音通知
-     *
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param string $fid         语音文件fid
-     * @param string $playtimes   播放次数,可选,最多3次,默认2次
-     * @param string $ext         用户的session内容,服务端原样返回,可选字段,不需要可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($nationCode, $phoneNumber, $fid, $playtimes = 2, $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-        $data->tel = $tel;
-        $data->fid = $fid;
-        $data->playtimes = $playtimes;
-
-        // app凭证
-        $data->sig = $this->util->calculateSig($this->appkey, $random,
-            $curTime, array($phoneNumber));
-
-        // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败
-        $data->time = $curTime;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 91
addons/qcloudsms/library/SmsMobileStatusPuller.php

@@ -1,91 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 拉取单个手机短信状态类
- *
- */
-class SmsMobileStatusPuller
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/pullstatus4mobile";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     * 拉取回执结果
-     *
-     * @param int    $type         拉取类型,0表示回执结果,1表示回复信息
-     * @param string $nationCode   国家码,如 86 为中国
-     * @param string $mobile       不带国家码的手机号
-     * @param int    $beginTime    开始时间(unix timestamp)
-     * @param int    $endTime      结束时间(unix timestamp)
-     * @param int    $max          拉取最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    private function pull($type, $nationCode, $mobile, $beginTime, $endTime, $max)
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        $data = new \stdClass();
-        $data->sig = $this->util->calculateSigForPuller($this->appkey, $random, $curTime);
-        $data->time = $curTime;
-        $data->type = $type;
-        $data->max = $max;
-        $data->begin_time = $beginTime;
-        $data->end_time = $endTime;
-        $data->nationcode = $nationCode;
-        $data->mobile = $mobile;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-
-    /**
-     * 拉取回执结果
-     *
-     * @param string $nationCode   国家码,如 86 为中国
-     * @param string $mobile       不带国家码的手机号
-     * @param int    $beginTime    开始时间(unix timestamp)
-     * @param int    $endTime      结束时间(unix timestamp)
-     * @param int    $max          拉取最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function pullCallback($nationCode, $mobile, $beginTime, $endTime, $max)
-    {
-        return $this->pull(0, $nationCode, $mobile, $beginTime, $endTime, $max);
-    }
-
-    /**
-     * 拉取回复信息
-     *
-     * @param string $nationCode   国家码,如 86 为中国
-     * @param string $mobile       不带国家码的手机号
-     * @param int    $beginTime    开始时间(unix timestamp)
-     * @param int    $endTime      结束时间(unix timestamp)
-     * @param int    $max          拉取最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function pullReply($nationCode, $mobile, $beginTime, $endTime, $max)
-    {
-        return $this->pull(1, $nationCode, $mobile, $beginTime, $endTime, $max);
-    }
-}

+ 0 - 99
addons/qcloudsms/library/SmsMultiSender.php

@@ -1,99 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 群发短信类
- *
- */
-class SmsMultiSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/sendmultisms2";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     * 普通群发
-     *
-     * 普通群发需明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中,
-     * 否则系统将使用默认签名。
-     *
-     *
-     * @param int    $type         短信类型,0 为普通短信,1 营销短信
-     * @param string $nationCode   国家码,如 86 为中国
-     * @param array  $phoneNumbers 不带国家码的手机号列表
-     * @param string $msg          信息内容,必须与申请的模板格式一致,否则将返回错误
-     * @param string $extend       扩展码,可填空串
-     * @param string $ext          服务端原样返回的参数,可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($type, $nationCode, $phoneNumbers, $msg, $extend = "", $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        $data = new \stdClass();
-        $data->tel = $this->util->phoneNumbersToArray($nationCode, $phoneNumbers);
-        $data->type = $type;
-        $data->msg = $msg;
-        $data->sig = $this->util->calculateSig($this->appkey, $random,
-            $curTime, $phoneNumbers);
-        $data->time = $curTime;
-        $data->extend = $extend;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-
-    /**
-     * 指定模板群发
-     *
-     *
-     * @param  string $nationCode   国家码,如 86 为中国
-     * @param  array  $phoneNumbers 不带国家码的手机号列表
-     * @param  int    $templId      模板id
-     * @param  array  $params       模板参数列表,如模板 {1}...{2}...{3},那么需要带三个参数
-     * @param  string $sign         签名,如果填空串,系统会使用默认签名
-     * @param  string $extend       扩展码,可填空串
-     * @param  string $ext          服务端原样返回的参数,可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function sendWithParam($nationCode, $phoneNumbers, $templId, $params,
-        $sign = "", $extend = "", $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        $data = new \stdClass();
-        $data->tel = $this->util->phoneNumbersToArray($nationCode, $phoneNumbers);
-        $data->sign = $sign;
-        $data->tpl_id = $templId;
-        $data->params = $params;
-        $data->sig = $this->util->calculateSigForTemplAndPhoneNumbers(
-            $this->appkey, $random, $curTime, $phoneNumbers);
-        $data->time = $curTime;
-        $data->extend = $extend;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 211
addons/qcloudsms/library/SmsSenderUtil.php

@@ -1,211 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-/**
- * 发送Util类
- *
- */
-class SmsSenderUtil
-{
-    /**
-     * 生成随机数
-     *
-     * @return int 随机数结果
-     */
-    public function getRandom()
-    {
-        return rand(100000, 999999);
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param string $appkey        sdkappid对应的appkey
-     * @param string $random        随机正整数
-     * @param string $curTime       当前时间
-     * @param array  $phoneNumbers  手机号码
-     * @return string  签名结果
-     */
-    public function calculateSig($appkey, $random, $curTime, $phoneNumbers)
-    {
-        $phoneNumbersString = $phoneNumbers[0];
-        for ($i = 1; $i < count($phoneNumbers); $i++) {
-            $phoneNumbersString .= ("," . $phoneNumbers[$i]);
-        }
-
-        return hash("sha256", "appkey=".$appkey."&random=".$random
-            ."&time=".$curTime."&mobile=".$phoneNumbersString);
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param string $appkey        sdkappid对应的appkey
-     * @param string $random        随机正整数
-     * @param string $curTime       当前时间
-     * @param array  $phoneNumbers  手机号码
-     * @return string  签名结果
-     */
-    public function calculateSigForTemplAndPhoneNumbers($appkey, $random,
-        $curTime, $phoneNumbers)
-    {
-        $phoneNumbersString = $phoneNumbers[0];
-        for ($i = 1; $i < count($phoneNumbers); $i++) {
-            $phoneNumbersString .= ("," . $phoneNumbers[$i]);
-        }
-
-        return hash("sha256", "appkey=".$appkey."&random=".$random
-            ."&time=".$curTime."&mobile=".$phoneNumbersString);
-    }
-
-    public function phoneNumbersToArray($nationCode, $phoneNumbers)
-    {
-        $i = 0;
-        $tel = array();
-        do {
-            $telElement = new \stdClass();
-            $telElement->nationcode = $nationCode;
-            $telElement->mobile = $phoneNumbers[$i];
-            array_push($tel, $telElement);
-        } while (++$i < count($phoneNumbers));
-
-        return $tel;
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param string $appkey        sdkappid对应的appkey
-     * @param string $random        随机正整数
-     * @param string $curTime       当前时间
-     * @param array  $phoneNumber   手机号码
-     * @return string  签名结果
-     */
-    public function calculateSigForTempl($appkey, $random, $curTime, $phoneNumber)
-    {
-        $phoneNumbers = array($phoneNumber);
-
-        return $this->calculateSigForTemplAndPhoneNumbers($appkey, $random,
-            $curTime, $phoneNumbers);
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param string $appkey        sdkappid对应的appkey
-     * @param string $random        随机正整数
-     * @param string $curTime       当前时间
-     * @return string 签名结果
-     */
-    public function calculateSigForPuller($appkey, $random, $curTime)
-    {
-        return hash("sha256", "appkey=".$appkey."&random=".$random
-            ."&time=".$curTime);
-    }
-
-    /**
-     * 生成上传文件授权
-     *
-     * @param string $appkey        sdkappid对应的appkey
-     * @param string $random        随机正整数
-     * @param string $curTime       当前时间
-     * @param array  $fileSha1Sum   文件sha1sum
-     * @return string  授权结果
-     */
-    public function calculateAuth($appkey, $random, $curTime, $fileSha1Sum)
-    {
-        return hash("sha256", "appkey=".$appkey."&random=".$random
-            ."&time=".$curTime."&content-sha1=".$fileSha1Sum);
-    }
-
-    /**
-     * 生成sha1sum
-     *
-     * @param string $content  内容
-     * @return string  内容sha1散列值
-     */
-    public function sha1sum($content)
-    {
-        return hash("sha1", $content);
-    }
-
-    /**
-     * 发送请求
-     *
-     * @param string $url      请求地址
-     * @param array  $dataObj  请求内容
-     * @return string 应答json字符串
-     */
-    public function sendCurlPost($url, $dataObj)
-    {
-        $curl = curl_init();
-        curl_setopt($curl, CURLOPT_URL, $url);
-        curl_setopt($curl, CURLOPT_HEADER, 0);
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
-        curl_setopt($curl, CURLOPT_POST, 1);
-        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60);
-        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($dataObj));
-        curl_setopt($curl, CURLOPT_HTTPHEADER, array(
-        'Content-Type: application/json; charset=utf-8',
-        'Content-Length: ' . strlen(json_encode($dataObj)))
-    );
-        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
-
-        $ret = curl_exec($curl);
-        if (false == $ret) {
-            // curl_exec failed
-            $result = "{ \"result\":" . -2 . ",\"errmsg\":\"" . curl_error($curl) . "\"}";
-        } else {
-            $rsp = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-            if (200 != $rsp) {
-                $result = "{ \"result\":" . -1 . ",\"errmsg\":\"". $rsp
-                        . " " . curl_error($curl) ."\"}";
-            } else {
-                $result = $ret;
-            }
-        }
-
-        curl_close($curl);
-
-        return $result;
-    }
-
-    /**
-     * 发送请求
-     *
-     * @param string $req  请求对象
-     * @return string 应答json字符串
-     */
-    public function fetch($req)
-    {
-        $curl = curl_init();
-
-        curl_setopt($curl, CURLOPT_URL, $req->url);
-        curl_setopt($curl, CURLOPT_HTTPHEADER, $req->headers);
-        curl_setopt($curl, CURLOPT_POSTFIELDS, $req->body);
-        curl_setopt($curl, CURLOPT_HEADER, 0);
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
-        curl_setopt($curl, CURLOPT_POST, 1);
-        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60);
-        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
-
-        $result = curl_exec($curl);
-
-        if (false == $result) {
-            // curl_exec failed
-            $result = "{ \"result\":" . -2 . ",\"errmsg\":\"" . curl_error($curl) . "\"}";
-        } else {
-            $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-            if (200 != $code) {
-                $result = "{ \"result\":" . -1 . ",\"errmsg\":\"". $rsp
-                    . " " . curl_error($curl) ."\"}";
-            }
-        }
-        curl_close($curl);
-
-        return $result;
-    }
-}

+ 0 - 107
addons/qcloudsms/library/SmsSingleSender.php

@@ -1,107 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 单发短信类
- *
- */
-class SmsSingleSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/sendsms";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     * 普通单发
-     *
-     * 普通单发需明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中,否则系统将使用默认签名。
-     *
-     * @param int    $type        短信类型,0 为普通短信,1 营销短信
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param string $msg         信息内容,必须与申请的模板格式一致,否则将返回错误
-     * @param string $extend      扩展码,可填空串
-     * @param string $ext         服务端原样返回的参数,可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($type, $nationCode, $phoneNumber, $msg, $extend = "", $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-
-        $data->tel = $tel;
-        $data->type = (int)$type;
-        $data->msg = $msg;
-        $data->sig = hash("sha256",
-            "appkey=".$this->appkey."&random=".$random."&time="
-            .$curTime."&mobile=".$phoneNumber, FALSE);
-        $data->time = $curTime;
-        $data->extend = $extend;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-
-    /**
-     * 指定模板单发
-     *
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param int    $templId     模板 id
-     * @param array  $params      模板参数列表,如模板 {1}...{2}...{3},那么需要带三个参数
-     * @param string $sign        签名,如果填空串,系统会使用默认签名
-     * @param string $extend      扩展码,可填空串
-     * @param string $ext         服务端原样返回的参数,可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function sendWithParam($nationCode, $phoneNumber, $templId = 0, $params,
-        $sign = "", $extend = "", $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-
-        $data->tel = $tel;
-        $data->sig = $this->util->calculateSigForTempl($this->appkey, $random,
-            $curTime, $phoneNumber);
-        $data->tpl_id = $templId;
-        $data->params = $params;
-        $data->sign = $sign;
-        $data->time = $curTime;
-        $data->extend = $extend;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 75
addons/qcloudsms/library/SmsStatusPuller.php

@@ -1,75 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 拉取短信状态类
- *
- */
-class SmsStatusPuller
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/pullstatus";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     * 拉取回执结果
-     *
-     * @param int $type 拉取类型,0表示回执结果,1表示回复信息
-     * @param int $max  最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    private function pull($type, $max)
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        $data = new \stdClass();
-        $data->sig = $this->util->calculateSigForPuller($this->appkey, $random, $curTime);
-        $data->time = $curTime;
-        $data->type = $type;
-        $data->max = $max;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-
-    /**
-     * 拉取回执结果
-     *
-     * @param int $max 拉取最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function pullCallback($max)
-    {
-        return $this->pull(0, $max);
-    }
-
-    /**
-     * 拉取回复信息
-     *
-     * @param int $max 拉取最大条数,最多100
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function pullReply($max)
-    {
-        return $this->pull(1, $max);
-    }
-}

+ 0 - 71
addons/qcloudsms/library/SmsVoicePromptSender.php

@@ -1,71 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 发送语音通知类
- *
- */
-class SmsVoicePromptSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlsvoicesvr/sendvoiceprompt";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     *
-     * 发送语音通知
-     *
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param string $prompttype  语音类型,目前固定为2
-     * @param string $msg         信息内容,必须与申请的模板格式一致,否则将返回错误
-     * @param string $playtimes   播放次数,可选,最多3次,默认2次
-     * @param string $ext         用户的session内容,服务端原样返回,可选字段,不需要可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($nationCode, $phoneNumber, $prompttype, $msg, $playtimes = 2, $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-
-        $data->tel = $tel;
-        // 通知内容,utf8编码,支持中文英文、数字及组合,需要和语音内容模版相匹配
-        $data->promptfile = $msg;
-        // 固定值 2
-        $data->prompttype = $prompttype;
-        $data->playtimes = $playtimes;
-        // app凭证
-        $data->sig = hash("sha256",
-            "appkey=".$this->appkey."&random=".$random."&time="
-            .$curTime."&mobile=".$phoneNumber, FALSE);
-        // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败
-        $data->time = $curTime;
-        $data->ext = $ext;
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 67
addons/qcloudsms/library/SmsVoiceVerifyCodeSender.php

@@ -1,67 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-/**
- * 发送语音验证码类
- *
- */
-class SmsVoiceVerifyCodeSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://yun.tim.qq.com/v5/tlsvoicesvr/sendvoice";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     * 发送语音验证码
-     *
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param string $msg         信息内容,必须与申请的模板格式一致,否则将返回错误
-     * @param int    $playtimes   播放次数,可选,最多3次,默认2次
-     * @param string $ext         用户的session内容,服务端原样返回,可选字段,不需要可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($nationCode, $phoneNumber, $msg, $playtimes = 2, $ext = "")
-    {
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-
-        $data->tel = $tel;
-        $data->msg = $msg;
-        $data->playtimes = $playtimes;
-        // app凭证
-        $data->sig = hash("sha256",
-            "appkey=".$this->appkey."&random=".$random."&time="
-            .$curTime."&mobile=".$phoneNumber, FALSE);
-        // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败
-        $data->time = $curTime;
-        $data->ext = $ext;
-
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 77
addons/qcloudsms/library/TtsVoiceSender.php

@@ -1,77 +0,0 @@
-<?php
-
-namespace addons\qcloudsms\library;
-
-use addons\qcloudsms\library\SmsSenderUtil;
-
-
-/**
- * 指定模板发送语音通知类
- *
- */
-class TtsVoiceSender
-{
-    private $url;
-    private $appid;
-    private $appkey;
-    private $util;
-
-    /**
-     * 构造函数
-     *
-     * @param string $appid  sdkappid
-     * @param string $appkey sdkappid对应的appkey
-     */
-    public function __construct($appid, $appkey)
-    {
-        $this->url = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendtvoice";
-        $this->appid =  $appid;
-        $this->appkey = $appkey;
-        $this->util = new SmsSenderUtil();
-    }
-
-    /**
-     *
-     * 指定模板发送语音短信
-     *
-     * @param string $nationCode  国家码,如 86 为中国
-     * @param string $phoneNumber 不带国家码的手机号
-     * @param int    $templId     模板 id
-     * @param array  $params      模板参数列表,如模板 {1}...{2}...{3},需要带三个参数
-     * @param string $playtimes   播放次数,可选,最多3次,默认2次
-     * @param string $ext         用户的session内容,服务端原样返回,可选字段,不需要可填空串
-     * @return string 应答json字符串,详细内容参见腾讯云协议文档
-     */
-    public function send($nationCode, $phoneNumber, $templId, $params, $playtimes = 2, $ext = "")
-    {
-        /*var_dump($nationCode);
-        var_dump($phoneNumber);
-        var_dump($templId);
-        var_dump($params);
-        var_dump($playtimes);
-        exit();*/
-        $random = $this->util->getRandom();
-        $curTime = time();
-        $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random;
-
-        // 按照协议组织 post 包体
-        $data = new \stdClass();
-        $tel = new \stdClass();
-        $tel->nationcode = "".$nationCode;
-        $tel->mobile = "".$phoneNumber;
-        $data->tel = $tel;
-        $data->tpl_id = $templId;
-        $data->params = $params;
-        $data->playtimes = $playtimes;
-
-        // app凭证
-        $data->sig = $this->util->calculateSig($this->appkey, $random,
-            $curTime, array($phoneNumber));
-
-        // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败
-        $data->time = $curTime;
-        $data->ext = $ext;
-        //var_dump($data);exit();
-        return $this->util->sendCurlPost($wholeUrl, $data);
-    }
-}

+ 0 - 1
addons/simditor/.addonrc

@@ -1 +0,0 @@
-{"license":"regular","licenseto":"39487","licensekey":"sNyrSchJ0XT49QMa dk0+rvAuiKzRZP8jTxi7Nw==","files":["public\\assets\\addons\\simditor\\css\\simditor.min.css","public\\assets\\addons\\simditor\\images\\image.png","public\\assets\\addons\\simditor\\js\\simditor.min.js"]}

+ 0 - 31
addons/simditor/Simditor.php

@@ -1,31 +0,0 @@
-<?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;
-    }
-
-}

File diff suppressed because it is too large
+ 0 - 0
addons/simditor/assets/css/simditor.min.css


BIN
addons/simditor/assets/images/image.png


File diff suppressed because it is too large
+ 0 - 0
addons/simditor/assets/js/simditor.min.js


+ 0 - 48
addons/simditor/bootstrap.js

@@ -1,48 +0,0 @@
-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;
-                });
-            });
-        }
-    }
-});

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

@@ -1,4 +0,0 @@
-#!/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

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

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

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

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

File diff suppressed because it is too large
+ 0 - 4699
addons/simditor/build/r.js


+ 0 - 4
addons/simditor/config.php

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

+ 0 - 10
addons/simditor/info.ini

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

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

@@ -1,460 +0,0 @@
-@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;
-  }
-}

File diff suppressed because it is too large
+ 0 - 2
addons/simditor/src/css/simditor.css


BIN
addons/simditor/src/images/image.png


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

@@ -1,241 +0,0 @@
-(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;
-
-}));
-

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

@@ -1,172 +0,0 @@
-(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;
-
-}));

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

@@ -1,5641 +0,0 @@
-(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;
-
-}));

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

@@ -1,261 +0,0 @@
-(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;
-
-}));

+ 29 - 0
application/admin/controller/Ad.php

@@ -3,6 +3,7 @@
 namespace app\admin\controller;
 
 use app\common\controller\Backend;
+use think\db\Query;
 
 /**
  * 
@@ -25,6 +26,34 @@ class Ad extends Backend
         $this->view->assign('pos',\app\common\model\Ad::$pos);
     }
 
+    public function index()
+    {
+        //设置过滤方法
+        $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();
+
+            $list = $this->model
+                ->where($where)
+                ->where(function (Query $query){
+                    if($this->admin('is_sub')){
+                        $query->where('admin_id',$this->auth->id);
+                    }
+                })
+                ->order($sort, $order)
+                ->paginate($limit);
+
+            $result = array("total" => $list->total(), "rows" => $list->items());
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+
     public function import()
     {
         parent::import();

+ 7 - 5
application/common/model/Ad.php

@@ -25,8 +25,8 @@ class Ad extends Model
     ];
     public static $pos=[
         'index'=>'首页',
-        'init'=>'启动页',
     ];
+    protected $readonly=['admin_id'];
     
 
     public static function getAd($pos=null){
@@ -37,8 +37,10 @@ class Ad extends Model
         return $q->select();
     }
 
-
-
-
-
+    protected static function init()
+    {
+        self::beforeInsert(function (self $ad){
+            $ad['admin_id']=$_SERVER['admin']['id'];
+        });
+    }
 }

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