<commit_before>/*
 * grunt-r3m c-less
 *
 * Based on the now deprecated grunt-less
 * original repo https://github.com/jachardi/grunt-less
 *
 * modified by royriojas@gmail.com to support copy of resources and concatenating of css files as well as less files
 *
 * Copyright (c) 2012 Jake Harding
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {
  'use strict';

  // Grunt utilities.

  var file = grunt.file,
    utils = grunt.util,
    verbose = grunt.verbose,
    log = require('../lib/log')(grunt),
    logVerbose = log.logVerbose,
    // external dependencies
    fs = require('fs'),
    path = require('path'),
    less = require('less'),
    lib = require('../lib/lib'),
    url = require('url'),
    format = lib.format,
    trim = lib.trim,
    md5 = lib.md5,
    addNoCache = lib.addNoCache;

  var URL_MATCHER = /url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/gi,  //regex used to match the urls inside the less or css files
    DATA_URI_MATCHER = /^data:/gi,                          //regex to test for an url with a data uri
    PROTOCOL_MATCHER = /^http|^https:\/\//gi,               //regex to test for an url with an absolute path
    RELATIVE_TO_HOST_MATCHER = /^\//g,                      //regex to test for an url relative to the host
    IS_LESS_MATCHER = /\.less$/;

  /**
   * test if a given path is a less file
   * @param path
   * @return {Boolean}
   */
  function isLessFile (path) {
    return IS_LESS_MATCHER.test(path);
  }

  function copyFileToNewLocation (src, destDir, relativePathToFile, version, rewritePathTemplate) {
    var dirOfFile = path.dirname(src);

    var urlObj = url.parse(relativePathToFile);
    var relativePath = lib.trim(urlObj.pathname);
    var lastPart = lib.trim(urlObj.search) + lib.trim(urlObj.hash);

    if (relativePath === '') {
      throw new Error('Not a valid url');
    }

    var absolutePathToResource = path.normalize(path.join(dirOfFile,relativePath));

    var md5OfResource = md5(absolutePathToResource);

    var fName = format('{0}', path.basename(relativePath));

    var relativeOutputFn = format(rewritePathTemplate, version , md5OfResource, fName);

    var newPath = path.normalize(path.join(destDir, relativeOutputFn));

    file.copy(absolutePathToResource, newPath);
    verbose.writeln(format('===> copied file from {0} to {1}', absolutePathToResource, newPath));
    var outName = relativeOutputFn + lastPart;
    verbose.writeln(format('===> url replaced from {0} to {1}', relativePathToFile, outName));
    return outName;
  }

  /**
   * check if this is a relative (to the document) url
   *
   * We need to rewrite if the url is relative. This function will return false if:
   * - starts with "/", cause it means it is relative to the main domain
   * - starts with "data:", cause it means is a data uri
   * - starts with a protocol
   *
   * in all other cases it will return true
   *
   * @param url the url to test
   * @returns {boolean}
   */
  function checkIfRelativePath (url) {
    return url.match(DATA_URI_MATCHER) ? false : url.match(RELATIVE_TO_HOST_MATCHER) ? false : !url.match(PROTOCOL_MATCHER);
  }

  function rewriteURLS (ctn, src, destDir, options) {

    var version = options.assetsVersion;
    var rewritePathTemplate = options.rewritePathTemplate;

    if (!lib.isNull(ctn)) {
      ctn = ctn.replace(URL_MATCHER, function (match, url) {
        url = trim(url);
        var needRewrite = checkIfRelativePath(url);

        if (needRewrite) {
          var pathToFile = copyFileToNewLocation(src, destDir, url, version, rewritePathTemplate);

          var outputPath = format('url({0})', pathToFile);
          verbose.writeln(format('===> This url will be transformed : {0} ==> {1}', url, outputPath));
          return outputPath;
        }

        return match;
      });

      ctn = options.processContent(ctn, src);
    }

    return ctn;
  }

  var lessProcess = function(srcFiles, destDir, options, callback) {

    var compileLESSFile = function (src, callback) {
      var defaults = lib.extend(options, {
        paths : []
      });

      defaults.paths.unshift(path.dirname(src));

      var parser = new less.Parser(defaults);

      var data = file.read(src);

      if (isLessFile(src)) {
        verbose.writeln('Parsing ' + src);
        // send data from source file to LESS parser to get CSS
        parser.parse(data, function (err, tree) {
          if (err) {
            callback(err);
          }

          var css = null;
          try {
            css = tree.toCSS({
              compress: options.compress,
              yuicompress: options.yuicompress
            });

            verbose.writeln('=========================================');
            verbose.writeln('Checking if require to rewrite the paths');
            css = rewriteURLS(css, src, destDir, options);

          } catch(e) {
            callback(e);
            return;
          }

          callback(null, css);
        });
      }
      else {
        data = rewriteURLS(data, src, destDir, options);
        callback(null, data);
      }

    };

    utils.async.map(srcFiles, compileLESSFile, function(err, results) {
      if (err) {
        callback(err);
        return;
      }

      callback(null, results.join(options.linefeed || ""));
    });
  };

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('cLess', 'Concatenate less or css resources.', function() {

    var me = this,
      data = me.data,
      src = data.src,
      dest = data.dest,
      options = me.options({
        assetsVersion : "",
        banner : '',
        dumpLineNumbers: "",
        linefeed : grunt.util.linefeed,
        rewritePathTemplate : 'assets/{0}/{1}/{2}',
        processContent : function (content, filePath) {
          return content;
        },
        postProcess : function (content, filePath) {
          return content;
        }
      });

    if (!src) {
      grunt.warn('Missing src property.');
      return false;
    }

    if (!dest) {
      grunt.warn('Missing dest property');
      return false;
    }

    var srcFiles = file.expand(src);

    logVerbose('Less src files = ' + srcFiles.join(', '));

    var destDir = path.dirname(dest);

    logVerbose('Less dest = ' + dest);

    var done = me.async();

    lessProcess(srcFiles, destDir, options, function(err, css) {
      if (err) {
        grunt.warn(JSON.stringify(err, null, 2));
        done(false);

        return;
      }

      options.banner && (css = options.banner + css);

      css = options.postProcess(css, dest);

      file.write(dest, css);
      done();
    });
    return true;
  });

};
<commit_msg>Added support for custom functions<commit_after>/*
 * grunt-r3m c-less
 *
 * Based on the now deprecated grunt-less
 * original repo https://github.com/jachardi/grunt-less
 *
 * modified by royriojas@gmail.com to support copy of resources and concatenating of css files as well as less files
 *
 * Copyright (c) 2012 Jake Harding
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {
  'use strict';

  // Grunt utilities.

  var file = grunt.file,
    utils = grunt.util,
    verbose = grunt.verbose,
    log = require('../lib/log')(grunt),
    logVerbose = log.logVerbose,
    // external dependencies
    fs = require('fs'),
    path = require('path'),
    less = require('less'),
    lib = require('../lib/lib'),
    url = require('url'),
    format = lib.format,
    trim = lib.trim,
    md5 = lib.md5,
    addNoCache = lib.addNoCache;

  var URL_MATCHER = /url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/gi,  //regex used to match the urls inside the less or css files
    DATA_URI_MATCHER = /^data:/gi,                          //regex to test for an url with a data uri
    PROTOCOL_MATCHER = /^http|^https:\/\//gi,               //regex to test for an url with an absolute path
    RELATIVE_TO_HOST_MATCHER = /^\//g,                      //regex to test for an url relative to the host
    IS_LESS_MATCHER = /\.less$/;

  /**
   * test if a given path is a less file
   * @param path
   * @return {Boolean}
   */
  function isLessFile (path) {
    return IS_LESS_MATCHER.test(path);
  }

  function copyFileToNewLocation (src, destDir, relativePathToFile, version, rewritePathTemplate) {
    var dirOfFile = path.dirname(src);

    var urlObj = url.parse(relativePathToFile);
    var relativePath = lib.trim(urlObj.pathname);
    var lastPart = lib.trim(urlObj.search) + lib.trim(urlObj.hash);

    if (relativePath === '') {
      throw new Error('Not a valid url');
    }

    var absolutePathToResource = path.normalize(path.join(dirOfFile,relativePath));

    var md5OfResource = md5(absolutePathToResource);

    var fName = format('{0}', path.basename(relativePath));

    var relativeOutputFn = format(rewritePathTemplate, version , md5OfResource, fName);

    var newPath = path.normalize(path.join(destDir, relativeOutputFn));

    file.copy(absolutePathToResource, newPath);
    verbose.writeln(format('===> copied file from {0} to {1}', absolutePathToResource, newPath));
    var outName = relativeOutputFn + lastPart;
    verbose.writeln(format('===> url replaced from {0} to {1}', relativePathToFile, outName));
    return outName;
  }

  /**
   * check if this is a relative (to the document) url
   *
   * We need to rewrite if the url is relative. This function will return false if:
   * - starts with "/", cause it means it is relative to the main domain
   * - starts with "data:", cause it means is a data uri
   * - starts with a protocol
   *
   * in all other cases it will return true
   *
   * @param url the url to test
   * @returns {boolean}
   */
  function checkIfRelativePath (url) {
    return url.match(DATA_URI_MATCHER) ? false : url.match(RELATIVE_TO_HOST_MATCHER) ? false : !url.match(PROTOCOL_MATCHER);
  }

  function rewriteURLS (ctn, src, destDir, options) {

    var version = options.assetsVersion;
    var rewritePathTemplate = options.rewritePathTemplate;

    if (!lib.isNull(ctn)) {
      ctn = ctn.replace(URL_MATCHER, function (match, url) {
        url = trim(url);
        var needRewrite = checkIfRelativePath(url);

        if (needRewrite) {
          var pathToFile = copyFileToNewLocation(src, destDir, url, version, rewritePathTemplate);

          var outputPath = format('url({0})', pathToFile);
          verbose.writeln(format('===> This url will be transformed : {0} ==> {1}', url, outputPath));
          return outputPath;
        }

        return match;
      });

      ctn = options.processContent(ctn, src);
    }

    return ctn;
  }

  var registerCustomFunctionInLess = function(tree, name, fn) {
    tree.functions[name] = function () {
      var returnOuput = fn.apply(this, arguments);
      return {
        toCSS : function (options) {
          return returnOutput;
        }
      }
    };
  }

  var lessProcess = function(srcFiles, destDir, options, callback) {

    var compileLESSFile = function (src, callback) {
      var defaults = lib.extend(options, {
        paths : [],
        filename: src
      });

      defaults.paths.unshift(path.dirname(src));
      var userFunctions = defaults.userFunctions;
      if (userFunctions) {
        var keys = Object.keys(userFunctions);
        keys.forEach(function (fName) {
          var fn = userFunctions[fName];
          registerCustomFunctionInLess(less.tree, fName, fn);
        });
      }

      var parser = new less.Parser(defaults);

      var data = file.read(src);

      if (isLessFile(src)) {
        verbose.writeln('Parsing ' + src);
        // send data from source file to LESS parser to get CSS
        parser.parse(data, function (err, tree) {
          if (err) {
            callback(err);
          }

          var css = null;
          try {
            css = tree.toCSS({
              compress: options.compress,
              yuicompress: options.yuicompress
            });

            verbose.writeln('=========================================');
            verbose.writeln('Checking if require to rewrite the paths');
            css = rewriteURLS(css, src, destDir, options);

          } catch(e) {
            callback(e);
            return;
          }

          callback(null, css);
        });
      }
      else {
        data = rewriteURLS(data, src, destDir, options);
        callback(null, data);
      }

    };

    utils.async.map(srcFiles, compileLESSFile, function(err, results) {
      if (err) {
        callback(err);
        return;
      }

      callback(null, results.join(options.linefeed || ""));
    });
  };

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('cLess', 'Concatenate less or css resources.', function() {

    var me = this,
      data = me.data,
      src = data.src,
      dest = data.dest,
      options = me.options({
        assetsVersion : "",
        banner : '',
        dumpLineNumbers: "",
        linefeed : grunt.util.linefeed,
        rewritePathTemplate : 'assets/{0}/{1}/{2}',
        processContent : function (content, filePath) {
          return content;
        },
        postProcess : function (content, filePath) {
          return content;
        }
      });

    if (!src) {
      grunt.warn('Missing src property.');
      return false;
    }

    if (!dest) {
      grunt.warn('Missing dest property');
      return false;
    }

    var srcFiles = file.expand(src);

    logVerbose('Less src files = ' + srcFiles.join(', '));

    var destDir = path.dirname(dest);

    logVerbose('Less dest = ' + dest);

    var done = me.async();

    lessProcess(srcFiles, destDir, options, function(err, css) {
      if (err) {
        grunt.warn(JSON.stringify(err, null, 2));
        done(false);

        return;
      }

      options.banner && (css = options.banner + css);

      css = options.postProcess(css, dest);

      file.write(dest, css);
      done();
    });
    return true;
  });

};
<|endoftext|>


<commit_before>var fs = require("fs");
var lib = require("./lib/index");
var path = require('path');

module.exports = function (grunt) {
    grunt.task.loadNpmTasks("grunt-contrib-clean");
    grunt.task.loadNpmTasks("grunt-contrib-concat");
    grunt.task.loadNpmTasks("grunt-contrib-copy");
    grunt.task.loadNpmTasks("grunt-contrib-cssmin");
    grunt.task.loadNpmTasks("grunt-contrib-uglify");
    grunt.task.loadNpmTasks("grunt-contrib-jshint");

    //恢复已经暂存的配置信息
    grunt.task.registerTask("easy-hybrid-rescue", "clean and rescue the old config", function () {
        grunt.config.init(grunt.config.get("easy-hybrid-rescue"));
    });

    //书写文件ID，将node.js模式的代码 转化成供loader加载的代码
    grunt.task.registerTask("easy-hybrid-rename", "rename file", function () {
        var config = grunt.config.get("easy-hybrid-rename");
        var deep = config.target.split("/").length;
        var match_type = new RegExp("^(cordova-)?" + config.platform + "-(.+)$");
        var match_url = /require *\( *['"](.+?)['"] *\)/ig;
        var mapping = grunt.file.expandMapping(["**", "!source/**"], config.target, {
            cwd: config.path,
            rename: function (dest, src) {
                return dest + src.split(/\/|\\/).map(function (item) {
                    return item.replace(match_type, "$1$2").replace(".html", "-tpl.js");
                }).join("/");
            }
        });
        mapping.forEach(function (item) {
            var src = item.src[0];
            var dest = item.dest;
            var arr = dest.split(/\/|\\/);
            var basename = arr.pop();
            if (basename.indexOf(".") < 0) {
                grunt.file.mkdir(dest);
                return;
            }
            var id = ("hybrid/" + arr.slice(deep - 1).join("/") + "/" + basename.split(".")[0]).replace(/\/+/g, "/");
            grunt.file.copy(src, dest, {
                encoding: "utf8",
                process: function (content) {
                    if (/\.html$/.test(src)) {
                        content = lib.run(content);
                    }
                    var lines = content.split("\n");
                    content = "    " + lines.join("\n    ");
                    return 'define("' + id + '", function (require, exports, module) {\n' + content.replace(match_url, function () {
                        var pfix = arguments[1].replace(config.platform + "-", "").replace(".html", "-tpl");
                        if (pfix.charAt(0) === ".") {
                            return ("require(\"" + path.join(id, "..", pfix).replace(/\\/g, "/") + "\")").replace("\\", "/");
                        }
                        return "require(\"" + pfix + "\")";
                    }) + '\n});'
                }
            });
        });
    });

    //生成index.js文件
    grunt.task.registerTask("easy-hybrid-index", function () {
        var config = grunt.config.get("easy-hybrid-index");
        var src = config.path;
        //生成index.js文件
        var content = 'define("hybrid/index", function (require, exports, module) {\n';
        content += "\n    //下边为加载patch代码\n";
        var list3 = grunt.file.expand({
            cwd: src + "patch/"
        }, "*.js");
        list3.forEach(function (item) {
            content += '    require("hybrid/patch/' + item.split(".")[0] + '");\n';
        });
        content += '\n    var core = require("hybrid/core");\n';

        function quickend(type) {
            content += '\n    //下边为' + type + '注册代码\n';
            var list = grunt.file.expand({
                cwd: src + type + "/"
            }, "*.js");
            list.forEach(function (item) {
                content += '    core.register("' + type + '", require("hybrid/' + type + "/" + item.split(".")[0] + '"));\n';
            });
        }

        [ "util", "plugin", "ui"].forEach(quickend);

        content += '\n    //下边为config注册代码\n';
        content += '\n    core.config = require("hybrid/config");\n';

        content += '\n    //下边为navigation注册代码\n';
        var list4 = grunt.file.expand({
            cwd: src + "/navigation/"
        }, "*.js");
        list4.forEach(function (item) {
            var id = item.split(".")[0];
            content += '    core.registerNavigation("' + id + '", require("hybrid/navigation/' + item.split(".")[0] + '")(core));\n';
        });

        content += '\n    //下边为view注册代码\n';
        var list2 = grunt.file.expand({
            cwd: src + "/view/"
        }, "*/*.js");
        list2.forEach(function (item) {
            content += '    core.registerView("' + item.split(".")[0] + '", require("hybrid/view/' + item.split(".")[0] + '"));\n';
        });

        content += '\n    //下边为初始化项目代码\n';
        if (config.proxy) {
            content += '\n    window.devProxy = "' + config.proxy + '";\n';
        }
        if (config.native) {
            content += '    window.cordova = require("cordova");\n';
            content += '    require("cordova/exec");\n';
        }
        content += '    exports.init = require("hybrid/init")(core);\n';
        content += '});';
        grunt.file.write(config.dest, content);
    });

    //根据配置生成index.html
    grunt.task.registerTask("easy-hybrid-build", function () {
        var config = grunt.config.get("easy-hybrid-build");
        var target = config.target;
        var src = config.path;
        var css = config.sources.css;
        var js = config.sources.js;
        var content = '\n    ' +
            '\n        <meta charset="utf-8"/>' +
            '\n        <base target="_self"/>' +
            '\n        <title>' + config.name + '</title>';
        css.forEach(function (item) {
            content += '\n        <link href="' + item + '" rel="stylesheet"/>';
        });
        content += '\n    \n    ';
        js.forEach(function (item) {
            content += '\n         ';
        });
        content += '\n    \n';
        grunt.file.write(target, content);
    });

    //修复easy-hybrid-build在开发版本时的错误
    grunt.task.registerTask("easy-hybrid-fix", function () {
        var config = grunt.config.get("easy-hybrid-build");
        var target = config.target;
        var src = config.path;
        var css = config.sources.css;
        var js = config.sources.js;
        js.push("js/require.js");
        grunt.file.expand({
            cwd: src + ""
        }, "css/*.css").forEach(function (item) {
            css.push(item);
        });
        grunt.file.expand({
            cwd: src + "compress/"
        }, "**/*.js").forEach(function (item) {
            js.push("js/" + item);
        });
        js.push("js/load.js");
        var content = '\n    ' +
            '\n        <meta charset="utf-8"/>' +
            '\n        <base target="_self"/>' +
            '\n        <title>' + config.name + '</title>';
        css.forEach(function (item) {
            content += '\n        <link href="' + item + '" rel="stylesheet"/>';
        });
        content += '\n    \n    ';
        js.forEach(function (item) {
            content += '\n         ';
        });
        content += '\n    \n';
        grunt.file.write(target, content);
    });

    //用于根据配置信息对一个平台进行编译
    grunt.task.registerMultiTask("easy-hybrid-platform", "fetch each platform", function () {
        var config = this.data;
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            copy: {
                "lib-css": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib + "css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "lib-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib,
                            src: ["**", "!css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "source-css": {
                    files: fs.readdirSync(config.src).filter(function (item) {
                        return grunt.file.isDir(process.cwd(), config.src, item);
                    }).map(function (item) {
                        return {
                            expand: true,
                            cwd: config.src + item + "/css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.source
                        };
                    })
                },
                "source-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.src,
                            src: ["**", "!**/css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.source
                        }
                    ]
                },
                "build": {
                    files: [
                        {
                            expand: true,
                            cwd: ".tmp/css/",
                            src: ["img/**"],
                            dest: config.build + "css/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/",
                            src: ["index.html"],
                            dest: config.build
                        },
                        {
                            expand: true,
                            cwd: ".tmp/js/source/" + (config.cordova ? "cordova" : "self") + "/",
                            src: config.native ? ["load.js", "require.js", "channel.js" , "cordova.js" , config.platform + "-exec.js" ] : ["load.js", "require.js"],
                            dest: ".tmp/"
                        }
                    ]
                }
            },
            "easy-hybrid-rename": {
                path: ".tmp/js/",
                platform: config.platform,
                target: ".tmp/compress/"
            },
            "easy-hybrid-index": {
                platform: config.platform,
                native: config.native,
                proxy: "",
                path: ".tmp/compress/",
                dest: ".tmp/compress/index.js"
            },
            "easy-hybrid-build": {
                path: ".tmp/",
                target: ".tmp/index.html",
                sources: config.sources,
                name: config.name
            },
            jshint: {
                all: [
                    '.tmp/compress/**/*.js'
                ],
                options: {
                    jshintrc: '.jshintrc'
                }
            },
            clean: {
                tmp: [".tmp/"],
                target: config.target
            },
            concat: {
                build: {
                    options: {
                        separator: '\n\n',
                        banner: '(function(){\n',
                        footer: '\n})();',
                        process: function (src, filepath) {
                            return '// Source: ' + filepath.replace("./tmp/compress/", "") + '\n' + src;
                        }
                    },
                    files: {
                        ".tmp/all.js": [".tmp/require.js", ".tmp/cordova.js", ".tmp/channel.js", ".tmp/" + config.platform + "-exec.js", ".tmp/compress/**/*.js", ".tmp/load.js"]
                    }
                }
            },
            uglify: {
                build: {
                    src: [".tmp/all.js"],
                    dest: config.build + "js/index.js"
                }
            },
            cssmin: {
                build: {
                    src: [".tmp/css/*.css"],
                    dest: config.build + "css/index.css"

                }
            }
        });
        var task = [
            "clean",
            "copy:lib-css",
            "copy:lib-js",
            "copy:source-css",
            "copy:source-js",
            "easy-hybrid-rename",
            "easy-hybrid-index",
            "easy-hybrid-build",
            "clean:target",
            "jshint",
            "copy:build",
            "concat",
            "uglify",
            "cssmin",
            "clean:tmp",
            "easy-hybrid-rescue"
        ];
        grunt.task.run(task);
    });

    //用于根据信息生成编译信息
    grunt.task.registerMultiTask("easy-hybrid-dev", "fetch each platform", function () {
        var config = this.data;
        if (config === false) {
            return;
        }
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            copy: {
                "lib-css": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib + "css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "lib-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib,
                            src: ["**", "!css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "source-css": {
                    files: fs.readdirSync(config.src).filter(function (item) {
                        return grunt.file.isDir(process.cwd(), config.src, item);
                    }).map(function (item) {
                        return {
                            expand: true,
                            cwd: config.src + item + "/css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.source
                        };
                    })
                },
                "source-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.src,
                            src: ["**", "!**/css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.source
                        }
                    ]
                },
                "build": {
                    files: [
                        {
                            expand: true,
                            cwd: ".tmp/css/",
                            src: ["**"],
                            dest: config.build + "css/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/compress/",
                            src: ["**"],
                            dest: config.build + "js/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/js/source/" + (config.cordova ? "cordova" : "self") + "/",
                            src: [ "load.js", "require.js"],
                            dest: config.build + "js/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/",
                            src: ["index.html"],
                            dest: config.build
                        }
                    ]
                }
            },
            "easy-hybrid-rename": {
                path: ".tmp/js/",
                platform: config.platform,
                target: ".tmp/compress/"
            },
            "easy-hybrid-index": {
                platform: config.platform,
                native: config.native,
                proxy: config.proxy,
                path: ".tmp/compress/",
                dest: ".tmp/compress/index.js"
            },
            "easy-hybrid-build": {
                path: ".tmp/",
                target: ".tmp/index.html",
                sources: config.sources,
                name: config.name
            },
            jshint: {
                all: [
                    '.tmp/compress/**/*.js'
                ],
                options: {
                    jshintrc: '.jshintrc'
                }
            },
            clean: {
                tmp: [".tmp/"],
                target: config.target
            }
        });
        var task = [
            "copy:lib-css",
            "copy:lib-js",
            "copy:source-css",
            "copy:source-js",
            "easy-hybrid-rename",
            "easy-hybrid-index",
            "easy-hybrid-fix",
            "clean:target",
            "jshint",
            "copy:build",
            "clean:tmp",
            "easy-hybrid-rescue"
        ];
        grunt.task.run(task);
    });

    //入口函数，用于产生新的配置文件，并对文件进行处理
    grunt.task.registerMultiTask("hybrid", "an javascript development approach base on cordova-js", function () {
        var me = this;
        //重新生成请求参数
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            "easy-hybrid-platform": lib.platformInit(me.target, me.data)
        });
        var task = ["easy-hybrid-platform"];
        var dev = lib.developInit(me.target, me.data);
        if (dev) {
            grunt.config.set("easy-hybrid-dev", {
                dev: dev
            });
            task.push("easy-hybrid-dev");
        }
        task.push("easy-hybrid-rescue");
        grunt.task.run(task);
    });
};
<commit_msg>update to version 0.1.5<commit_after>var fs = require("fs");
var lib = require("./lib/index");
var path = require('path');

module.exports = function (grunt) {
    grunt.task.loadNpmTasks("grunt-contrib-clean");
    grunt.task.loadNpmTasks("grunt-contrib-concat");
    grunt.task.loadNpmTasks("grunt-contrib-copy");
    grunt.task.loadNpmTasks("grunt-contrib-cssmin");
    grunt.task.loadNpmTasks("grunt-contrib-uglify");
    grunt.task.loadNpmTasks("grunt-contrib-jshint");

    //恢复已经暂存的配置信息
    grunt.task.registerTask("easy-hybrid-rescue", "clean and rescue the old config", function () {
        grunt.config.init(grunt.config.get("easy-hybrid-rescue"));
    });

    //书写文件ID，将node.js模式的代码 转化成供loader加载的代码
    grunt.task.registerTask("easy-hybrid-rename", "rename file", function () {
        var config = grunt.config.get("easy-hybrid-rename");
        var deep = config.target.split("/").length;
        var match_type = new RegExp("^(cordova-)?" + config.platform + "-(.+)$");
        var match_url = /require *\( *['"](.+?)['"] *\)/ig;
        var mapping = grunt.file.expandMapping(["**", "!source/**"], config.target, {
            cwd: config.path,
            rename: function (dest, src) {
                return dest + src.split(/\/|\\/).map(function (item) {
                    return item.replace(match_type, "$1$2").replace(".html", "-tpl.js");
                }).join("/");
            }
        });
        mapping.forEach(function (item) {
            var src = item.src[0];
            var dest = item.dest;
            var arr = dest.split(/\/|\\/);
            var basename = arr.pop();
            if (basename.indexOf(".") < 0) {
                grunt.file.mkdir(dest);
                return;
            }
            var id = ("hybrid/" + arr.slice(deep - 1).join("/") + "/" + basename.split(".")[0]).replace(/\/+/g, "/");
            grunt.file.copy(src, dest, {
                encoding: "utf8",
                process: function (content) {
                    if (/\.html$/.test(src)) {
                        content = lib.run(content);
                    }
                    var lines = content.split("\n");
                    content = "    " + lines.join("\n    ");
                    return 'define("' + id + '", function (require, exports, module) {\n' + content.replace(match_url, function () {
                        var pfix = arguments[1].replace(config.platform + "-", "").replace(".html", "-tpl");
                        if (pfix.charAt(0) === ".") {
                            return ("require(\"" + path.join(id, "..", pfix).replace(/\\/g, "/") + "\")").replace("\\", "/");
                        }
                        return "require(\"" + pfix + "\")";
                    }) + '\n});'
                }
            });
        });
    });

    //生成index.js文件
    grunt.task.registerTask("easy-hybrid-index", function () {
        var config = grunt.config.get("easy-hybrid-index");
        var src = config.path;
        //生成index.js文件
        var content = 'define("hybrid/index", function (require, exports, module) {\n';
        content += "\n    //下边为加载patch代码\n";
        var list3 = grunt.file.expand({
            cwd: src + "patch/"
        }, "*.js");
        list3.forEach(function (item) {
            content += '    require("hybrid/patch/' + item.split(".")[0] + '");\n';
        });
        content += '\n    var core = require("hybrid/core");\n';

        function quickend(type) {
            content += '\n    //下边为' + type + '注册代码\n';
            var list = grunt.file.expand({
                cwd: src + type + "/"
            }, "*.js");
            list.forEach(function (item) {
                content += '    core.register("' + type + '", require("hybrid/' + type + "/" + item.split(".")[0] + '"));\n';
            });
        }

        [ "util", "plugin", "ui"].forEach(quickend);

        content += '\n    //下边为config注册代码\n';
        content += '\n    core.config = require("hybrid/config");\n';

        content += '\n    //下边为navigation注册代码\n';
        var list4 = grunt.file.expand({
            cwd: src + "/navigation/"
        }, "*.js");
        list4.forEach(function (item) {
            var id = item.split(".")[0];
            content += '    core.registerNavigation("' + id + '", require("hybrid/navigation/' + item.split(".")[0] + '")(core));\n';
        });

        content += '\n    //下边为view注册代码\n';
        var list2 = grunt.file.expand({
            cwd: src + "/view/"
        }, "*/*.js");
        list2.forEach(function (item) {
            content += '    core.registerView("' + item.split(".")[0] + '", require("hybrid/view/' + item.split(".")[0] + '"));\n';
        });

        content += '\n    //下边为初始化项目代码\n';
        if (config.proxy) {
            content += '\n    window.devProxy = "' + config.proxy + '";\n';
        }
        if (config.native) {
            content += '    window.cordova = require("cordova");\n';
            content += '    require("cordova/exec");\n';
        }
        content += '    exports.init = require("hybrid/init")(core);\n';
        content += '});';
        grunt.file.write(config.dest, content);
    });

    //根据配置生成index.html
    grunt.task.registerTask("easy-hybrid-build", function () {
        var config = grunt.config.get("easy-hybrid-build");
        var target = config.target;
        var src = config.path;
        var css = config.sources.css;
        var js = config.sources.js;
        var content = '\n    ' +
            '\n        <meta charset="utf-8"/>' +
            '\n        <base target="_self"/>' +
            '\n        <title>' + config.name + '</title>';
        css.forEach(function (item) {
            content += '\n        <link href="' + item + '" rel="stylesheet"/>';
        });
        content += '\n    \n    ';
        js.forEach(function (item) {
            content += '\n         ';
        });
        content += '\n    \n';
        grunt.file.write(target, content);
    });

    //修复easy-hybrid-build在开发版本时的错误
    grunt.task.registerTask("easy-hybrid-fix", function () {
        var config = grunt.config.get("easy-hybrid-build");
        var target = config.target;
        var src = config.path;
        var css = config.sources.css;
        var js = config.sources.js;
        js.push("js/require.js");
        grunt.file.expand({
            cwd: src + ""
        }, "css/*.css").forEach(function (item) {
            css.push(item);
        });
        grunt.file.expand({
            cwd: src + "compress/"
        }, "**/*.js").forEach(function (item) {
            js.push("js/" + item);
        });
        js.push("js/load.js");
        var content = '\n    ' +
            '\n        <meta charset="utf-8"/>' +
            '\n        <base target="_self"/>' +
            '\n        <title>' + config.name + '</title>';
        css.forEach(function (item) {
            content += '\n        <link href="' + item + '" rel="stylesheet"/>';
        });
        content += '\n    \n    ';
        js.forEach(function (item) {
            content += '\n         ';
        });
        content += '\n    \n';
        grunt.file.write(target, content);
    });

    //用于根据配置信息对一个平台进行编译
    grunt.task.registerMultiTask("easy-hybrid-platform", "fetch each platform", function () {
        var config = this.data;
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            copy: {
                "lib-css": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib + "css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "lib-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib,
                            src: ["**", "!css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "source-css": {
                    files: [
                        {
                            expand: true,
                            flatten: true,
                            cwd: config.src,
                            src: ["**/*.css"],
                            dest: ".tmp/css/",
                            filter: config.filter.source

                        },
                        {
                            expand: true,
                            flatten: true,
                            cwd: config.src,
                            src: ["**/img/*.*"],
                            dest: ".tmp/css/img/",
                            filter: config.filter.source

                        }
                    ]
                },
                "source-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.src,
                            src: ["**", "!**/css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.source
                        }
                    ]
                },
                "build": {
                    files: [
                        {
                            expand: true,
                            cwd: ".tmp/css/",
                            src: ["img/**"],
                            dest: config.build + "css/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/",
                            src: ["index.html"],
                            dest: config.build
                        },
                        {
                            expand: true,
                            cwd: ".tmp/js/source/" + (config.cordova ? "cordova" : "self") + "/",
                            src: config.native ? ["load.js", "require.js", "channel.js" , "cordova.js" , config.platform + "-exec.js" ] : ["load.js", "require.js"],
                            dest: ".tmp/"
                        }
                    ]
                }
            },
            "easy-hybrid-rename": {
                path: ".tmp/js/",
                platform: config.platform,
                target: ".tmp/compress/"
            },
            "easy-hybrid-index": {
                platform: config.platform,
                native: config.native,
                proxy: "",
                path: ".tmp/compress/",
                dest: ".tmp/compress/index.js"
            },
            "easy-hybrid-build": {
                path: ".tmp/",
                target: ".tmp/index.html",
                sources: config.sources,
                name: config.name
            },
            jshint: {
                all: [
                    '.tmp/compress/**/*.js'
                ],
                options: {
                    jshintrc: '.jshintrc'
                }
            },
            clean: {
                tmp: [".tmp/"],
                target: config.target
            },
            concat: {
                build: {
                    options: {
                        separator: '\n\n',
                        banner: '(function(){\n',
                        footer: '\n})();',
                        process: function (src, filepath) {
                            return '// Source: ' + filepath.replace("./tmp/compress/", "") + '\n' + src;
                        }
                    },
                    files: {
                        ".tmp/all.js": [".tmp/require.js", ".tmp/cordova.js", ".tmp/channel.js", ".tmp/" + config.platform + "-exec.js", ".tmp/compress/**/*.js", ".tmp/load.js"]
                    }
                }
            },
            uglify: {
                build: {
                    src: [".tmp/all.js"],
                    dest: config.build + "js/index.js"
                }
            },
            cssmin: {
                build: {
                    src: [".tmp/css/*.css"],
                    dest: config.build + "css/index.css"

                }
            }
        });
        var task = [
            "clean",
            "copy:lib-css",
            "copy:lib-js",
            "copy:source-css",
            "copy:source-js",
            "easy-hybrid-rename",
            "easy-hybrid-index",
            "easy-hybrid-build",
            "clean:target",
            "jshint",
            "copy:build",
            "concat",
            "uglify",
            "cssmin",
            "clean:tmp",
            "easy-hybrid-rescue"
        ];
        grunt.task.run(task);
    });

    //用于根据信息生成编译信息
    grunt.task.registerMultiTask("easy-hybrid-dev", "fetch each platform", function () {
        var config = this.data;
        if (config === false) {
            return;
        }
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            copy: {
                "lib-css": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib + "css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "lib-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.lib,
                            src: ["**", "!css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.lib
                        }
                    ]
                },
                "source-css": {
                    files: fs.readdirSync(config.src).filter(function (item) {
                        return grunt.file.isDir(process.cwd(), config.src, item);
                    }).map(function (item) {
                        return {
                            expand: true,
                            cwd: config.src + item + "/css/",
                            src: ["**"],
                            dest: ".tmp/css/",
                            filter: config.filter.source
                        };
                    })
                },
                "source-js": {
                    files: [
                        {
                            expand: true,
                            cwd: config.src,
                            src: ["**", "!**/css/**"],
                            dest: ".tmp/js/",
                            filter: config.filter.source
                        }
                    ]
                },
                "build": {
                    files: [
                        {
                            expand: true,
                            cwd: ".tmp/css/",
                            src: ["**"],
                            dest: config.build + "css/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/compress/",
                            src: ["**"],
                            dest: config.build + "js/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/js/source/" + (config.cordova ? "cordova" : "self") + "/",
                            src: [ "load.js", "require.js"],
                            dest: config.build + "js/"
                        },
                        {
                            expand: true,
                            cwd: ".tmp/",
                            src: ["index.html"],
                            dest: config.build
                        }
                    ]
                }
            },
            "easy-hybrid-rename": {
                path: ".tmp/js/",
                platform: config.platform,
                target: ".tmp/compress/"
            },
            "easy-hybrid-index": {
                platform: config.platform,
                native: config.native,
                proxy: config.proxy,
                path: ".tmp/compress/",
                dest: ".tmp/compress/index.js"
            },
            "easy-hybrid-build": {
                path: ".tmp/",
                target: ".tmp/index.html",
                sources: config.sources,
                name: config.name
            },
            jshint: {
                all: [
                    '.tmp/compress/**/*.js'
                ],
                options: {
                    jshintrc: '.jshintrc'
                }
            },
            clean: {
                tmp: [".tmp/"],
                target: config.target
            }
        });
        var task = [
            "copy:lib-css",
            "copy:lib-js",
            "copy:source-css",
            "copy:source-js",
            "easy-hybrid-rename",
            "easy-hybrid-index",
            "easy-hybrid-fix",
            "clean:target",
            "jshint",
            "copy:build",
            "clean:tmp",
            "easy-hybrid-rescue"
        ];
        grunt.task.run(task);
    });

    //入口函数，用于产生新的配置文件，并对文件进行处理
    grunt.task.registerMultiTask("hybrid", "an javascript development approach base on cordova-js", function () {
        var me = this;
        //重新生成请求参数
        grunt.config.init({
            "easy-hybrid-rescue": grunt.config.get(),//缓存现有文件
            "easy-hybrid-platform": lib.platformInit(me.target, me.data)
        });
        var task = ["easy-hybrid-platform"];
        var dev = lib.developInit(me.target, me.data);
        if (dev) {
            grunt.config.set("easy-hybrid-dev", {
                dev: dev
            });
            task.push("easy-hybrid-dev");
        }
        task.push("easy-hybrid-rescue");
        grunt.task.run(task);
    });
};
<|endoftext|>


<commit_before>function main() {
    // Since we don't have the fallback of attachEvent and
    // other IE only stuff we won't try to run JS for IE.
    // It will run though when using Google Chrome Frame
    if (document.all) { return; }

    var currentSlideNo;
    var notesOn = false;
    var expanded = false;
    var hiddenContext = false;
    var blanked = false;
    var slides = document.getElementsByClassName('slide');
    var touchStartX = 0;
    var spaces = /\s+/, a1 = [''];
    var tocOpened = false;
    var helpOpened = false;
    var overviewActive = false;
    var modifierKeyDown = false;
    var scale = 1;
    var showingPresenterView = false;
    var presenterViewWin = null;
    var isPresenterView = false;

    var str2array = function(s) {
        if (typeof s == 'string' || s instanceof String) {
            if (s.indexOf(' ') < 0) {
                a1[0] = s;
                return a1;
            } else {
                return s.split(spaces);
            }
        }
        return s;
    };

    var trim = function(str) {
        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    };

    var addClass = function(node, classStr) {
        classStr = str2array(classStr);
        var cls = ' ' + node.className + ' ';
        for (var i = 0, len = classStr.length, c; i < len; ++i) {
            c = classStr[i];
            if (c && cls.indexOf(' ' + c + ' ') < 0) {
                cls += c + ' ';
            }
        }
        node.className = trim(cls);
    };

    var removeClass = function(node, classStr) {
        var cls;
        if (!node) {
            throw 'no node provided';
        }
        if (classStr !== undefined) {
            classStr = str2array(classStr);
            cls = ' ' + node.className + ' ';
            for (var i = 0, len = classStr.length; i < len; ++i) {
                cls = cls.replace(' ' + classStr[i] + ' ', ' ');
            }
            cls = trim(cls);
        } else {
            cls = '';
        }
        if (node.className != cls) {
            node.className = cls;
        }
    };

    var getSlideEl = function(slideNo) {
        if (slideNo > 0) {
            return slides[slideNo - 1];
        } else {
            return null;
        }
    };

    var getSlideTitle = function(slideNo) {
        var el = getSlideEl(slideNo);
        if (el) {
            var headers = el.getElementsByTagName('header');
            if (headers.length > 0) {
                return el.getElementsByTagName('header')[0].innerText;
            }
        }
        return null;
    };

    var getSlidePresenterNote = function(slideNo) {
        var el = getSlideEl(slideNo);
        if (el) {
            var n = el.getElementsByClassName('presenter_notes');
            if (n.length > 0) {
                return n[0];
            }
        }
        return null;
    };

    var changeSlideElClass = function(slideNo, className) {
        var el = getSlideEl(slideNo);
        if (el) {
            removeClass(el, 'far-past past current future far-future none');
            addClass(el, className);
        }
    };

    var updateSlideClasses = function(updateOther) {
        window.location.hash = (isPresenterView ? "presenter:" : "slide:") + currentSlideNo;

        for (var i=1; i<currentSlideNo-1; i++) {
            changeSlideElClass(i, 'far-past');
        }

        changeSlideElClass(currentSlideNo - 1, 'past');
        changeSlideElClass(currentSlideNo, 'current');
        changeSlideElClass(currentSlideNo + 1, 'future');
        changeSlideElClass(currentSlideNo + 2, 'far-future');

        for (i=currentSlideNo+3; i<slides.length+1; i++) {
            changeSlideElClass(i, 'none');
        }

        highlightCurrentTocLink();

        document.getElementsByTagName('title')[0].innerText = getSlideTitle(currentSlideNo);

        updatePresenterNotes();

        if (updateOther) { updateOtherPage(); }
    };

    var updatePresenterNotes = function() {
        if (!isPresenterView) { return; }

        var existingNote = document.getElementById('current_presenter_notes');
        var currentNote = getSlidePresenterNote(currentSlideNo).cloneNode(true);
        currentNote.setAttribute('id', 'presenter_note');

        existingNote.replaceChild(currentNote, document.getElementById('presenter_note'));
    };

    var highlightCurrentTocLink = function() {
        var toc = document.getElementById('toc');

        if (toc) {
            var tocRows = toc.getElementsByTagName('tr');
            for (var i=0; i<tocRows.length; i++) {
                removeClass(tocRows.item(i), 'active');
            }

            var currentTocRow = document.getElementById('toc-row-' + currentSlideNo);
            if (currentTocRow) {
                addClass(currentTocRow, 'active');
            }
        }
    };

    var updateOtherPage = function() {
        if (!showingPresenterView) { return; }

        var w = isPresenterView ? window.opener : presenterViewWin;
        w.postMessage('slide#' + currentSlideNo, '*');
    };

    var nextSlide = function() {
        if (currentSlideNo < slides.length) {
            currentSlideNo++;
        }
        updateSlideClasses(true);
    };

    var prevSlide = function() {
        if (currentSlideNo > 1) {
            currentSlideNo--;
        }
        updateSlideClasses(true);
    };

    var showNotes = function() {
        var slide = getSlideEl(currentSlideNo),
            notes = slide.getElementsByClassName('presenter_notes');
        for (var i = 0, len = notes.length; i < len; i++) {
            notes.item(i).style.display = (notesOn) ? 'none':'block';
        }
        notesOn = !notesOn;
        if (notesOn) {
            addClass(slide, 'presenter_notes');
        } else {
            removeClass(slide, 'presenter_notes');
        }
    };

    var showSlideNumbers = function() {
        var asides = document.getElementsByClassName('page_number');
        var hidden = asides[0].style.display != 'block';
        for (var i = 0; i < asides.length; i++) {
            asides.item(i).style.display = hidden ? 'block' : 'none';
        }
    };

    var showSlideSources = function() {
        var asides = document.getElementsByClassName('source');
        var hidden = asides[0].style.display != 'block';
        for (var i = 0; i < asides.length; i++) {
            asides.item(i).style.display = hidden ? 'block' : 'none';
        }
    };

    var showToc = function() {
        if (helpOpened) {
                showHelp();
        }
        var toc = document.getElementById('toc');
        if (toc) {
            toc.style.marginLeft = tocOpened ? '-' + (toc.clientWidth + 20) + 'px' : '0px';
            tocOpened = !tocOpened;
        }
        updateOverview();
    };

    var showHelp = function() {
        if (tocOpened) {
                showToc();
        }

        var help = document.getElementById('help');

        if (help) {
            help.style.marginLeft = helpOpened ? '-' + (help.clientWidth + 20) + 'px' : '0px';
            helpOpened = !helpOpened;
        }
    };

    var showPresenterView = function() {
        if (isPresenterView) { return; }

        if (showingPresenterView) {
            if (presenterViewWin)
                presenterViewWin.close();
            presenterViewWin = null;
            showingPresenterView = false;
        } else {
            presenterViewWin = open(window.location.pathname + "#presenter:" + currentSlideNo, 'presenter_notes',
                                    'directories=no,location=no,toolbar=no,menubar=no,copyhistory=no');
            showingPresenterView = true;
        }
    };

    var toggleOverview = function() {
        if (!overviewActive) {
            addClass(document.body, 'expose');
            overviewActive = true;
            setScale(1);
        } else {
            removeClass(document.body, 'expose');
            overviewActive = false;
            if (expanded) {
                setScale(scale);    // restore scale
            }
        }
        updateOverview();
    };

    var updateOverview = function() {
        try {
            var presentation = document.getElementsByClassName('presentation')[0];
        } catch (e) {
            return;
        }

        if (isPresenterView) {
            var action = overviewActive ? removeClass : addClass;
            action(document.body, 'presenter_view');
        }

        var toc = document.getElementById('toc');

        if (!toc) {
            return;
        }

        if (!tocOpened || !overviewActive) {
            presentation.style.marginLeft = '0px';
            presentation.style.width = '100%';
        } else {
            presentation.style.marginLeft = toc.clientWidth + 'px';
            presentation.style.width = (presentation.clientWidth - toc.clientWidth) + 'px';
        }
    };

    var computeScale = function() {
        var cSlide = document.getElementsByClassName('current')[0];
        var sx = (cSlide.clientWidth + 20) / window.innerWidth;
        var sy = (cSlide.clientHeight + 20) / window.innerHeight;
        return 1 / Math.max(sy, sx);
    };

    var setScale = function(scale) {
        var presentation = document.getElementsByClassName('slides')[0];
        var transform = 'scale(' + scale + ')';
        presentation.style.MozTransform = transform;
        presentation.style.WebkitTransform = transform;
        presentation.style.OTransform = transform;
        presentation.style.msTransform = transform;
        presentation.style.transform = transform;
    };

    var expandSlides = function() {
        if (overviewActive) {
            return;
        }
        if (expanded) {
            setScale(1);
            showContext();
            expanded = false;
        } else {
            setExpanded();
        }
    };
    var setExpanded = function() {
        scale = computeScale();
        setScale(scale);
        expanded = true;
        hideContext();
    };

    var showContext = function() {
        var presentation = document.getElementsByClassName('slides')[0];
        removeClass(presentation, 'nocontext');
        hiddenContext = true;
    };

    var hideContext = function() {
        if (isPresenterView) { return; }
        var presentation = document.getElementsByClassName('slides')[0];
        addClass(presentation, 'nocontext');
        hiddenContext = false;
    };

    var toggleContext = function() {
        if (hiddenContext) {
            hideContext();
        } else {
            showContext();
        }
    };

    var toggleBlank = function() {
        blank_elem = document.getElementById('blank');

        blank_elem.style.display = blanked ? 'none' : 'block';

        blanked = !blanked;
    };

    var isModifierKey = function(keyCode) {
        switch (keyCode) {
            case 16: // shift
            case 17: // ctrl
            case 18: // alt
            case 91: // command
                return true;
                break;
            default:
                return false;
                break;
        }
    };

    var checkModifierKeyUp = function(event) {
        if (isModifierKey(event.keyCode)) {
            modifierKeyDown = false;
        }
    };

    var checkModifierKeyDown = function(event) {
        if (isModifierKey(event.keyCode)) {
            modifierKeyDown = true;
        }
    };

    var handleBodyKeyDown = function(event) {
        if (modifierKeyDown && event.keyCode == 80) {
            setScale(1);
            showContext();
            expanded = false;
        }
        if (modifierKeyDown) {
            return
        }
        switch (event.keyCode) {
            case 13: // Enter
                if (overviewActive) {
                    toggleOverview();
                }
                break;
            case 27: // ESC
                toggleOverview();
                break;
            case 37: // left arrow
            case 33: // page up
                event.preventDefault();
                prevSlide();
                break;
            case 39: // right arrow
            case 32: // space
            case 34: // page down
                event.preventDefault();
                nextSlide();
                break;
            case 50: // 2
                showNotes();
                break;
            case 190: // .
            case 48: // 0
            case 66: // b
                if (!overviewActive) {
                    toggleBlank();
                }
                break;
            case 67: // c
                if (!overviewActive) {
                    toggleContext();
                }
                break;
            case 69: // e
                if (!overviewActive) {
                    expandSlides();
                }
                break;
            case 72: // h
                showHelp();
                break;
            case 78: // n
                if (!overviewActive) {
                    showSlideNumbers();
                }
                break;
            case 80: // p
                if (!overviewActive) {
                    showPresenterView();
                }
                break;
            case 83: // s
                if (!overviewActive) {
                    showSlideSources();
                }
                break;
            case 84: // t
                showToc();
                break;
        }
    };

    var handleWheel = function(event) {
        if (tocOpened || helpOpened || overviewActive) {
            return;
        }

        var delta = 0;

        if (!event) {
            event = window.event;
        }

        if (event.wheelDelta) {
            delta = event.wheelDelta/120;
            if (window.opera) delta = -delta;
        } else if (event.detail) {
            delta = -event.detail/3;
        }

        if (delta && delta <0) {
            nextSlide();
        } else if (delta) {
            prevSlide();
        }
    };

    var addSlideClickListeners = function() {
        for (var i=0; i < slides.length; i++) {
            var slide = slides.item(i);
            slide.num = i + 1;
            slide.addEventListener('click', function(e) {
                if (overviewActive) {
                    currentSlideNo = this.num;
                    toggleOverview();
                    updateSlideClasses(true);
                    e.preventDefault();
                }
                return false;
            }, true);
        }
    };

    var addRemoteWindowControls = function() {
        window.addEventListener("message", function(e) {
            if (e.data.indexOf("slide#") != -1) {
                    currentSlideNo = Number(e.data.replace('slide#', ''));
                    updateSlideClasses(false);
            }
        }, false);
    };

    var addTouchListeners = function() {
        document.addEventListener('touchstart', function(e) {
            touchStartX = e.touches[0].pageX;
        }, false);
        document.addEventListener('touchend', function(e) {
            var pixelsMoved = touchStartX - e.changedTouches[0].pageX;
            var SWIPE_SIZE = 150;
            if (pixelsMoved > SWIPE_SIZE) {
                nextSlide();
            }
            else if (pixelsMoved < -SWIPE_SIZE) {
             prevSlide();
            }
        }, false);
    };

    var addTocLinksListeners = function() {
        var toc = document.getElementById('toc');
        if (toc) {
            var tocLinks = toc.getElementsByTagName('a');
            for (var i=0; i < tocLinks.length; i++) {
                tocLinks.item(i).addEventListener('click', function(e) {
                    currentSlideNo = Number(this.attributes['href'].value.replace('#slide:', ''));
                    updateSlideClasses(true);
                    e.preventDefault();
                }, true);
            }
        }
    };

    // initialize

    (function() {
        if (window.location.hash == "") {
            currentSlideNo = 1;
        } else if (window.location.hash.indexOf("#presenter:") != -1) {
            currentSlideNo = Number(window.location.hash.replace('#presenter:', ''));
            isPresenterView = true;
            showingPresenterView = true;
            presenterViewWin = window;
            addClass(document.body, 'presenter_view');
        } else {
            currentSlideNo = Number(window.location.hash.replace('#slide:', '')) || 1;
        }

        document.addEventListener('keyup', checkModifierKeyUp);
        document.addEventListener('keydown', handleBodyKeyDown);
        document.addEventListener('keydown', checkModifierKeyDown);
        document.addEventListener('visibilitychange', function(event) {
            if (document.hidden) {
                modifierKeyDown = false;
            }
        }, false);
        setInterval(function() {
            if (!document.hasFocus()) {
                modifierKeyDown = false;
            }
        }, 100);

        window.addEventListener("mousewheel", handleWheel);
        window.addEventListener("DOMMouseScroll", handleWheel);
        window.addEventListener("DOMContentLoaded", expandSlides);
        window.onresize = function(){
            setScale(!overviewActive && expanded ? scale = computeScale() : 1);
        }

        for (var i = 0, el; el = slides[i]; i++) {
            addClass(el, 'slide far-future');
        }
        updateSlideClasses(false);

        // add support for finger events (filter it by property detection?)
        addTouchListeners();
        addTocLinksListeners();
        addSlideClickListeners();
        addRemoteWindowControls();
    })();
}
<commit_msg>Fix bunch of glitches when toc/help are open (some things should not be allowed when those are open).<commit_after>function main() {
    // Since we don't have the fallback of attachEvent and
    // other IE only stuff we won't try to run JS for IE.
    // It will run though when using Google Chrome Frame
    if (document.all) { return; }

    var currentSlideNo;
    var notesOn = false;
    var expanded = false;
    var hiddenContext = false;
    var blanked = false;
    var slides = document.getElementsByClassName('slide');
    var touchStartX = 0;
    var spaces = /\s+/, a1 = [''];
    var tocOpened = false;
    var helpOpened = false;
    var overviewActive = false;
    var modifierKeyDown = false;
    var scale = 1;
    var showingPresenterView = false;
    var presenterViewWin = null;
    var isPresenterView = false;

    var str2array = function(s) {
        if (typeof s == 'string' || s instanceof String) {
            if (s.indexOf(' ') < 0) {
                a1[0] = s;
                return a1;
            } else {
                return s.split(spaces);
            }
        }
        return s;
    };

    var trim = function(str) {
        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    };

    var addClass = function(node, classStr) {
        classStr = str2array(classStr);
        var cls = ' ' + node.className + ' ';
        for (var i = 0, len = classStr.length, c; i < len; ++i) {
            c = classStr[i];
            if (c && cls.indexOf(' ' + c + ' ') < 0) {
                cls += c + ' ';
            }
        }
        node.className = trim(cls);
    };

    var removeClass = function(node, classStr) {
        var cls;
        if (!node) {
            throw 'no node provided';
        }
        if (classStr !== undefined) {
            classStr = str2array(classStr);
            cls = ' ' + node.className + ' ';
            for (var i = 0, len = classStr.length; i < len; ++i) {
                cls = cls.replace(' ' + classStr[i] + ' ', ' ');
            }
            cls = trim(cls);
        } else {
            cls = '';
        }
        if (node.className != cls) {
            node.className = cls;
        }
    };

    var getSlideEl = function(slideNo) {
        if (slideNo > 0) {
            return slides[slideNo - 1];
        } else {
            return null;
        }
    };

    var getSlideTitle = function(slideNo) {
        var el = getSlideEl(slideNo);
        if (el) {
            var headers = el.getElementsByTagName('header');
            if (headers.length > 0) {
                return el.getElementsByTagName('header')[0].innerText;
            }
        }
        return null;
    };

    var getSlidePresenterNote = function(slideNo) {
        var el = getSlideEl(slideNo);
        if (el) {
            var n = el.getElementsByClassName('presenter_notes');
            if (n.length > 0) {
                return n[0];
            }
        }
        return null;
    };

    var changeSlideElClass = function(slideNo, className) {
        var el = getSlideEl(slideNo);
        if (el) {
            removeClass(el, 'far-past past current future far-future none');
            addClass(el, className);
        }
    };

    var updateSlideClasses = function(updateOther) {
        window.location.hash = (isPresenterView ? "presenter:" : "slide:") + currentSlideNo;

        for (var i=1; i<currentSlideNo-1; i++) {
            changeSlideElClass(i, 'far-past');
        }

        changeSlideElClass(currentSlideNo - 1, 'past');
        changeSlideElClass(currentSlideNo, 'current');
        changeSlideElClass(currentSlideNo + 1, 'future');
        changeSlideElClass(currentSlideNo + 2, 'far-future');

        for (i=currentSlideNo+3; i<slides.length+1; i++) {
            changeSlideElClass(i, 'none');
        }

        highlightCurrentTocLink();

        document.getElementsByTagName('title')[0].innerText = getSlideTitle(currentSlideNo);

        updatePresenterNotes();

        if (updateOther) { updateOtherPage(); }
    };

    var updatePresenterNotes = function() {
        if (!isPresenterView) { return; }

        var existingNote = document.getElementById('current_presenter_notes');
        var currentNote = getSlidePresenterNote(currentSlideNo).cloneNode(true);
        currentNote.setAttribute('id', 'presenter_note');

        existingNote.replaceChild(currentNote, document.getElementById('presenter_note'));
    };

    var highlightCurrentTocLink = function() {
        var toc = document.getElementById('toc');

        if (toc) {
            var tocRows = toc.getElementsByTagName('tr');
            for (var i=0; i<tocRows.length; i++) {
                removeClass(tocRows.item(i), 'active');
            }

            var currentTocRow = document.getElementById('toc-row-' + currentSlideNo);
            if (currentTocRow) {
                addClass(currentTocRow, 'active');
            }
        }
    };

    var updateOtherPage = function() {
        if (!showingPresenterView) { return; }

        var w = isPresenterView ? window.opener : presenterViewWin;
        w.postMessage('slide#' + currentSlideNo, '*');
    };

    var nextSlide = function() {
        if (tocOpened || helpOpened || overviewActive) {
            return;
        }
        if (currentSlideNo < slides.length) {
            currentSlideNo++;
        }
        updateSlideClasses(true);
    };

    var prevSlide = function() {
        if (tocOpened || helpOpened || overviewActive) {
            return;
        }
        if (currentSlideNo > 1) {
            currentSlideNo--;
        }
        updateSlideClasses(true);
    };

    var showNotes = function() {
        var slide = getSlideEl(currentSlideNo),
            notes = slide.getElementsByClassName('presenter_notes');
        for (var i = 0, len = notes.length; i < len; i++) {
            notes.item(i).style.display = (notesOn) ? 'none':'block';
        }
        notesOn = !notesOn;
        if (notesOn) {
            addClass(slide, 'presenter_notes');
        } else {
            removeClass(slide, 'presenter_notes');
        }
    };

    var showSlideNumbers = function() {
        var asides = document.getElementsByClassName('page_number');
        var hidden = asides[0].style.display != 'block';
        for (var i = 0; i < asides.length; i++) {
            asides.item(i).style.display = hidden ? 'block' : 'none';
        }
    };

    var showSlideSources = function() {
        var asides = document.getElementsByClassName('source');
        var hidden = asides[0].style.display != 'block';
        for (var i = 0; i < asides.length; i++) {
            asides.item(i).style.display = hidden ? 'block' : 'none';
        }
    };

    var showToc = function() {
        if (helpOpened) {
            showHelp();
        }
        var toc = document.getElementById('toc');
        if (toc) {
            toc.style.marginLeft = tocOpened ? '-' + (toc.clientWidth + 20) + 'px' : '0px';
            tocOpened = !tocOpened;
        }
        updateOverview();
    };

    var showHelp = function() {
        if (tocOpened) {
            showToc();
        }

        var help = document.getElementById('help');

        if (help) {
            help.style.marginLeft = helpOpened ? '-' + (help.clientWidth + 20) + 'px' : '0px';
            helpOpened = !helpOpened;
        }
    };

    var showPresenterView = function() {
        if (isPresenterView) { return; }

        if (showingPresenterView) {
            if (presenterViewWin)
                presenterViewWin.close();
            presenterViewWin = null;
            showingPresenterView = false;
        } else {
            presenterViewWin = open(window.location.pathname + "#presenter:" + currentSlideNo, 'presenter_notes',
                                    'directories=no,location=no,toolbar=no,menubar=no,copyhistory=no');
            showingPresenterView = true;
        }
    };

    var toggleOverview = function() {
        if (helpOpened) {
            showHelp();
            return
        }
        if (tocOpened) {
            showToc();
            return;
        }
        if (!overviewActive) {
            addClass(document.body, 'expose');
            overviewActive = true;
            setScale(1);
        } else {
            removeClass(document.body, 'expose');
            overviewActive = false;
            if (expanded) {
                setScale(scale);    // restore scale
            }
        }
        updateOverview();
    };

    var updateOverview = function() {
        try {
            var presentation = document.getElementsByClassName('presentation')[0];
        } catch (e) {
            return;
        }

        if (isPresenterView) {
            var action = overviewActive ? removeClass : addClass;
            action(document.body, 'presenter_view');
        }

        var toc = document.getElementById('toc');

        if (!toc) {
            return;
        }

        if (!tocOpened || !overviewActive) {
            presentation.style.marginLeft = '0px';
            presentation.style.width = '100%';
        } else {
            presentation.style.marginLeft = toc.clientWidth + 'px';
            presentation.style.width = (presentation.clientWidth - toc.clientWidth) + 'px';
        }
    };

    var computeScale = function() {
        var cSlide = document.getElementsByClassName('current')[0];
        var sx = (cSlide.clientWidth + 20) / window.innerWidth;
        var sy = (cSlide.clientHeight + 20) / window.innerHeight;
        return 1 / Math.max(sy, sx);
    };

    var setScale = function(scale) {
        var presentation = document.getElementsByClassName('slides')[0];
        var transform = 'scale(' + scale + ')';
        presentation.style.MozTransform = transform;
        presentation.style.WebkitTransform = transform;
        presentation.style.OTransform = transform;
        presentation.style.msTransform = transform;
        presentation.style.transform = transform;
    };

    var expandSlides = function() {
        if (overviewActive) {
            return;
        }
        if (expanded) {
            setScale(1);
            showContext();
            expanded = false;
        } else {
            setExpanded();
        }
    };
    var setExpanded = function() {
        scale = computeScale();
        setScale(scale);
        expanded = true;
        hideContext();
    };

    var showContext = function() {
        var presentation = document.getElementsByClassName('slides')[0];
        removeClass(presentation, 'nocontext');
        hiddenContext = true;
    };

    var hideContext = function() {
        if (isPresenterView) { return; }
        var presentation = document.getElementsByClassName('slides')[0];
        addClass(presentation, 'nocontext');
        hiddenContext = false;
    };

    var toggleContext = function() {
        if (hiddenContext) {
            hideContext();
        } else {
            showContext();
        }
    };

    var toggleBlank = function() {
        blank_elem = document.getElementById('blank');

        blank_elem.style.display = blanked ? 'none' : 'block';

        blanked = !blanked;
    };

    var isModifierKey = function(keyCode) {
        switch (keyCode) {
            case 16: // shift
            case 17: // ctrl
            case 18: // alt
            case 91: // command
                return true;
                break;
            default:
                return false;
                break;
        }
    };

    var checkModifierKeyUp = function(event) {
        if (isModifierKey(event.keyCode)) {
            modifierKeyDown = false;
        }
    };

    var checkModifierKeyDown = function(event) {
        if (isModifierKey(event.keyCode)) {
            modifierKeyDown = true;
        }
    };

    var handleBodyKeyDown = function(event) {
        if (modifierKeyDown && event.keyCode == 80) {
            setScale(1);
            showContext();
            expanded = false;
        }
        if (modifierKeyDown) {
            return
        }
        switch (event.keyCode) {
            case 13: // Enter
                if (overviewActive) {
                    toggleOverview();
                }
                break;
            case 27: // ESC
                toggleOverview();
                break;
            case 37: // left arrow
            case 33: // page up
                event.preventDefault();
                prevSlide();
                break;
            case 39: // right arrow
            case 32: // space
            case 34: // page down
                event.preventDefault();
                nextSlide();
                break;
            case 50: // 2
                showNotes();
                break;
            case 190: // .
            case 48: // 0
            case 66: // b
                if (!overviewActive) {
                    toggleBlank();
                }
                break;
            case 67: // c
                if (!overviewActive) {
                    toggleContext();
                }
                break;
            case 69: // e
                if (!overviewActive) {
                    expandSlides();
                }
                break;
            case 72: // h
                showHelp();
                break;
            case 78: // n
                if (!overviewActive) {
                    showSlideNumbers();
                }
                break;
            case 80: // p
                if (!overviewActive) {
                    showPresenterView();
                }
                break;
            case 83: // s
                if (!overviewActive) {
                    showSlideSources();
                }
                break;
            case 84: // t
                showToc();
                break;
        }
    };

    var handleWheel = function(event) {
        if (tocOpened || helpOpened || overviewActive) {
            return;
        }

        var delta = 0;

        if (!event) {
            event = window.event;
        }

        if (event.wheelDelta) {
            delta = event.wheelDelta/120;
            if (window.opera) delta = -delta;
        } else if (event.detail) {
            delta = -event.detail/3;
        }

        if (delta && delta <0) {
            nextSlide();
        } else if (delta) {
            prevSlide();
        }
    };

    var addSlideClickListeners = function() {
        for (var i=0; i < slides.length; i++) {
            var slide = slides.item(i);
            slide.num = i + 1;
            slide.addEventListener('click', function(e) {
                if (overviewActive) {
                    currentSlideNo = this.num;
                    toggleOverview();
                    updateSlideClasses(true);
                    e.preventDefault();
                }
                return false;
            }, true);
        }
    };

    var addRemoteWindowControls = function() {
        window.addEventListener("message", function(e) {
            if (e.data.indexOf("slide#") != -1) {
                    currentSlideNo = Number(e.data.replace('slide#', ''));
                    updateSlideClasses(false);
            }
        }, false);
    };

    var addTouchListeners = function() {
        document.addEventListener('touchstart', function(e) {
            touchStartX = e.touches[0].pageX;
        }, false);
        document.addEventListener('touchend', function(e) {
            var pixelsMoved = touchStartX - e.changedTouches[0].pageX;
            var SWIPE_SIZE = 150;
            if (pixelsMoved > SWIPE_SIZE) {
                nextSlide();
            }
            else if (pixelsMoved < -SWIPE_SIZE) {
             prevSlide();
            }
        }, false);
    };

    var addTocLinksListeners = function() {
        var toc = document.getElementById('toc');
        if (toc) {
            var tocLinks = toc.getElementsByTagName('a');
            for (var i=0; i < tocLinks.length; i++) {
                tocLinks.item(i).addEventListener('click', function(e) {
                    currentSlideNo = Number(this.attributes['href'].value.replace('#slide:', ''));
                    updateSlideClasses(true);
                    e.preventDefault();
                }, true);
            }
        }
    };

    // initialize

    (function() {
        if (window.location.hash == "") {
            currentSlideNo = 1;
        } else if (window.location.hash.indexOf("#presenter:") != -1) {
            currentSlideNo = Number(window.location.hash.replace('#presenter:', ''));
            isPresenterView = true;
            showingPresenterView = true;
            presenterViewWin = window;
            addClass(document.body, 'presenter_view');
        } else {
            currentSlideNo = Number(window.location.hash.replace('#slide:', '')) || 1;
        }

        document.addEventListener('keyup', checkModifierKeyUp);
        document.addEventListener('keydown', handleBodyKeyDown);
        document.addEventListener('keydown', checkModifierKeyDown);
        document.addEventListener('visibilitychange', function(event) {
            if (document.hidden) {
                modifierKeyDown = false;
            }
        }, false);
        setInterval(function() {
            if (!document.hasFocus()) {
                modifierKeyDown = false;
            }
        }, 100);

        window.addEventListener("mousewheel", handleWheel);
        window.addEventListener("DOMMouseScroll", handleWheel);
        window.addEventListener("DOMContentLoaded", expandSlides);
        window.onresize = function(){
            setScale(!overviewActive && expanded ? scale = computeScale() : 1);
        }

        for (var i = 0, el; el = slides[i]; i++) {
            addClass(el, 'slide far-future');
        }
        updateSlideClasses(false);

        // add support for finger events (filter it by property detection?)
        addTouchListeners();
        addTocLinksListeners();
        addSlideClickListeners();
        addRemoteWindowControls();
    })();
}
<|endoftext|>