<commit_before>/* Copyright 2007-2008 by Oliver Steele.  Available under the MIT License. */

MOP = {
    /** For each name in `methods`, defines a method on `target` with this
      * name, that delegates to the method of the `propertyName` property of
      * `target` with the same name. */
    delegate: function(target, propertyName, methods) {
        for (var i = 0; i < methods.length; i++)
            addMethod(methods[i]);
        function addMethod(methodName) {
            target[methodName] = function() {
                var delegate = this[propertyName];
                return delegate[methodName].apply(delegate, arguments);
            }
        }
    },

    /** When a new MethodReplacer is constructed, it replaces
      * each method on `object` by the method in `methods`
      * with the same key value, if such a method exists.
      * A MethodReplacer has a single method, `restoreMethods`,
      * which restores each method to its pre-replacement
      * value. */
    MethodReplacer: function(object, methods) {
        var savedMethods = {};
        replaceMethods();
        this.restore = restoreMethods;

        function replaceMethods() {
            for (var name in methods) {
                savedMethods[name] = object[name];
                object[name] = methods[name];
            }
        }
        function restoreMethods() {
            for (var name in methods) {
                delete object[name];
                object[name] == savedMethods[name] ||
                    (object[name] = savedMethods[name]);
            }
        }
    },

    /** When a new QueueBall is constructed, it replaces each
      * method named by `methodNames` with a method that enqueues
      * the method call (the name of the method and its arguments).
      * A QueueBall has a single method, `replayMethodCalls`, which
      * plays back the method calls and restores the methods.
      */
    QueueBall: function(object, methodNames) {
        var savedMethods = {},
            queue = [];
        replaceMethods();
        this.replayMethodCalls = replayMethodCalls;

        function replaceMethods() {
            for (var ix in methodNames) {
                var name = methodNames[ix];
                savedMethods[name] = object[name];
                object[name] = makeInterceptor(name);
            }
        }
        function restoreMethods() {
            for (var ix in methodNames) {
                var name = methodNames[ix];
                delete object[name];
                object[name] == savedMethods[name] ||
                    (object[name] = savedMethods[name]);
            }
        }
        function makeInterceptor(name) {
            return function() {
                var item = [name].concat(Array.prototype.slice.call(arguments, 0));
                queue.push(item);
            }
        }
        function replayMethodCalls() {
            restoreMethods();
            while (queue.length) {
                var item = queue.shift(),
                    name = item.shift();
                object[name].apply(object, item);
            }
        }
    },

    /** Calls `fn` on `object`, within a dynamic scope within
      * which the methods in `methods` have temporarily replaced
      * the like-named methods on `object`.
      * The scope is terminated by the argument to the call to
      * `fn`; this argument should be treated as a continuation,
      * and restores the methods. */
    withMethodOverridesCallback: function(object, methods, fn) {
        if (!methods) return fn.call(object, function() {});
        var replacer = new MOP.MethodReplacer(object, methods);
        return fn.call(object, replacer.restore);
    },

    /** Calls `fn` on `object`, within a dynamic scope within
      * which the methods in `methodNames` have been enqueued.
      * The scope is terminated by the argument to the call to
      * `fn`; this argument should be treated as a continuation,
      * and ends the queue, replaying the methods. */
    withDeferredMethods: function(object, methodNames, fn) {
        var qball = new MOP.QueueBall(object, methodNames);
        return fn.call(object, qball.replayMethodCalls);
    }
}
<commit_msg>Object.method + Class.method methods<commit_after>/* Copyright 2007-2008 by Oliver Steele.  Available under the MIT License. */

MOP = {
    Object: function(object) {
        var definers = {
            accessor: function(propertyName) {
                var capitalized = name.slice(0, 1).toUpperCase() + name.slice(1);
                object['get' + capitalized] = function() { return this[name] };
                object['set' + capitalized] = function(value) { this[name] = value; };
            },
            getter: function(propertyName) {
                var capitalized = name.slice(0, 1).toUpperCase() + name.slice(1);
                object['get' + capitalized] = function() { return this[name] }
            },
            /** For each name in `methods`, defines a method on
            `object` with this name, that delegates to the method of
            the `propertyName` property of `object` with the same
            name. */
            delegate: function(propertyName, methods) {
                for (var i = 0; i < methods.length; i++)
                    addMethod(methods[i]);
                function addMethod(methodName) {
                    object[methodName] = function() {
                        var delegate = this[propertyName];
                        return delegate[methodName].apply(delegate, arguments);
                    }
                }
            }
        };
        function method(methodName) {
            var fn = object[methodName];
            return {
                guardBy: function(guard) {
                    return replaceBy(function() {
                        return guard() && fn.apply(this, arguments);
                    });
                },
                guardUntil: function(guard, ms) {
                    ms = ms || 1000;
                    return replaceBy(function() {
                        var self = this,
                            args = Array.prototype.slice.call(arguments, 0),
                            timer = setInterval(function() {
                                if (!guard()) return;
                                clearInterval(timer);
                                restore();
                                fn.apply(self, args);
                            }, ms);
                        return timer;
                    });
                },
                trace: function() {
                    return replaceBy(function() {
                        var result = fn.apply(this, arguments);
                        console.info(methodName, result);
                        return result;
                    });
                },
                time: function() {
                    return replaceBy(function() {
                        var t0 = new Date,
                            result = fn.apply(this, arguments),
                            t1 = new Date;
                        console.info(methodName, t1 - t0);
                        return result;
                    });
                }
            }
            function replaceBy(newFn) {
                object[methodName] = newFn;
                newFn.remove = restore;
                return method(methodName);
            }
            function restore() { object[methodName] = fn }
        }
        var modifiers = {
            time: function(methodName) {
                var fn = object[methodName];
                object[methodName] = function() {
                    var t0 = new Date,
                        result = fn.apply(this, arguments),
                        t1 = new Date;
                    console.info(methodName, t1 - t0);
                    return result;
                }
            }
        };
        return { define: definers, method: method, methods: modifiers };
    },

    Class: function(klass) { return Object(klass.prototype) },

    /** When a new MethodReplacer is constructed, it replaces
      * each method on `object` by the method in `methods`
      * with the same key value, if such a method exists.
      * A MethodReplacer has a single method, `restoreMethods`,
      * which restores each method to its pre-replacement
      * value. */
    MethodReplacer: function(object, methods) {
        var savedMethods = {};
        replaceMethods();
        this.restore = restoreMethods;

        function replaceMethods() {
            for (var name in methods) {
                savedMethods[name] = object[name];
                object[name] = methods[name];
            }
        }
        function restoreMethods() {
            for (var name in methods) {
                delete object[name];
                object[name] == savedMethods[name] ||
                    (object[name] = savedMethods[name]);
            }
        }
    },

    /** When a new QueueBall is constructed, it replaces each
      * method named by `methodNames` with a method that enqueues
      * the method call (the name of the method and its arguments).
      * A QueueBall has a single method, `replayMethodCalls`, which
      * plays back the method calls and restores the methods.
      */
    QueueBall: function(object, methodNames) {
        var savedMethods = {},
            queue = [];
        replaceMethods();
        this.replayMethodCalls = replayMethodCalls;

        function replaceMethods() {
            for (var ix in methodNames) {
                var name = methodNames[ix];
                savedMethods[name] = object[name];
                object[name] = makeInterceptor(name);
            }
        }
        function restoreMethods() {
            for (var ix in methodNames) {
                var name = methodNames[ix];
                delete object[name];
                object[name] == savedMethods[name] ||
                    (object[name] = savedMethods[name]);
            }
        }
        function makeInterceptor(name) {
            return function() {
                var item = [name].concat(Array.prototype.slice.call(arguments, 0));
                queue.push(item);
            }
        }
        function replayMethodCalls() {
            restoreMethods();
            while (queue.length) {
                var item = queue.shift(),
                    name = item.shift();
                object[name].apply(object, item);
            }
        }
    },

    /** Calls `fn` on `object`, within a dynamic scope within
      * which the methods in `methods` have temporarily replaced
      * the like-named methods on `object`.
      * The scope is terminated by the argument to the call to
      * `fn`; this argument should be treated as a continuation,
      * and restores the methods. */
    withMethodOverridesCallback: function(object, methods, fn) {
        if (!methods) return fn.call(object, function() {});
        var replacer = new MOP.MethodReplacer(object, methods);
        return fn.call(object, replacer.restore);
    },

    /** Calls `fn` on `object`, within a dynamic scope within
      * which the methods in `methodNames` have been enqueued.
      * The scope is terminated by the argument to the call to
      * `fn`; this argument should be treated as a continuation,
      * and ends the queue, replaying the methods. */
    withDeferredMethods: function(object, methodNames, fn) {
        var qball = new MOP.QueueBall(object, methodNames);
        return fn.call(object, qball.replayMethodCalls);
    }
}
<|endoftext|>


<commit_before>var _ = require('lodash');

function isQuizNode(node) {
    return (/^[(\[][ x][)\]]/).test(node.text || node);
}

function isTableQuestion(nodes) {
    var block = questionBlock(nodes);
    return (
        block.length === 1 &&
        block[0].type === 'table' &&
        _.all(block[0].cells[0].slice(1), isQuizNode)
    );
}

function isListQuestion(nodes) {
    var block = questionBlock(nodes);
    // Counter of when we go in and out of lists
    var inlist = 0;
    // Number of lists we found
    var lists = 0;
    // Elements found outside a list
    var outsiders = 0;
    // Ensure that we have nothing except lists
    _.each(block, function(node) {
        if(node.type === 'list_start') {
            inlist++;
        } else if(node.type === 'list_end') {
            inlist--;
            lists++;
        } else if(inlist === 0) {
            // Found non list_start or list_end whilst outside a list
            outsiders++;
        }
    });
    return lists > 0 && outsiders === 0;
}

function isQuestion(nodes) {
    return isListQuestion(nodes) || isTableQuestion(nodes);
}

// Remove (optional) paragraph header node and blockquote
function questionBlock(nodes) {
    return nodes.slice(
        nodes[0].type === 'paragraph' ? 1 : 0,
        _.findIndex(nodes, { type: 'blockquote_start' })
    );
}

function splitQuestions(nodes) {
    // Represents nodes in current question
    var buffer = [];
    return _.reduce(nodes, function(accu, node) {
        // Add node to buffer
        buffer.push(node);

        // Flush buffer once we hit the end of a question
        if(node.type === 'blockquote_end') {
            accu.push(buffer);
            // Clear buffer
            buffer = [];
        }

        return accu;
    }, []);
}

function isQuiz(nodes) {
    // Extract potential questions
    var questions = splitQuestions(nodes);

    // Nothing that looks like questions
    if(questions.length === 0) {
        return false;
    }

    // Ensure all questions are correctly structured
    return _.all(questions, isQuestion);
}

module.exports = isQuiz;
<commit_msg>Allow quiz titles in validation<commit_after>var _ = require('lodash');

function isQuizNode(node) {
    return (/^[(\[][ x][)\]]/).test(node.text || node);
}

function isTableQuestion(nodes) {
    var block = questionBlock(nodes);
    return (
        block.length === 1 &&
        block[0].type === 'table' &&
        _.all(block[0].cells[0].slice(1), isQuizNode)
    );
}

function isListQuestion(nodes) {
    var block = questionBlock(nodes);
    // Counter of when we go in and out of lists
    var inlist = 0;
    // Number of lists we found
    var lists = 0;
    // Elements found outside a list
    var outsiders = 0;
    // Ensure that we have nothing except lists
    _.each(block, function(node) {
        if(node.type === 'list_start') {
            inlist++;
        } else if(node.type === 'list_end') {
            inlist--;
            lists++;
        } else if(inlist === 0) {
            // Found non list_start or list_end whilst outside a list
            outsiders++;
        }
    });
    return lists > 0 && outsiders === 0;
}

function isQuestion(nodes) {
    return isListQuestion(nodes) || isTableQuestion(nodes);
}

// Remove (optional) paragraph header node and blockquote
function questionBlock(nodes) {
    return nodes.slice(
        nodes[0].type === 'paragraph' ? 1 : 0,
        _.findIndex(nodes, { type: 'blockquote_start' })
    );
}

function splitQuestions(nodes) {
    // Represents nodes in current question
    var buffer = [];
    return _.reduce(nodes, function(accu, node) {
        // Add node to buffer
        buffer.push(node);

        // Flush buffer once we hit the end of a question
        if(node.type === 'blockquote_end') {
            accu.push(buffer);
            // Clear buffer
            buffer = [];
        }

        return accu;
    }, []);
}

function isQuiz(nodes) {
    // Extract potential questions
    var questions = splitQuestions(
        // Skip quiz title if there
        nodes.slice(
            (nodes[0] && nodes[0].type) === 'paragraph' ? 1 : 0
        )
    );

    // Nothing that looks like questions
    if(questions.length === 0) {
        return false;
    }

    // Ensure all questions are correctly structured
    return _.all(questions, isQuestion);
}

module.exports = isQuiz;
<|endoftext|>


<commit_before>'use strict';

var expectFile = require('gulp-expect-file');
var streamSync = require('./stream-sync');
var through = require('through2').obj;

module.exports = function (gulp, gutil) {

  function allDirTreePaths(paths) {
    var newPaths = [];
    for (var i = 0; i < paths.length; i++) {
      var dirs = paths[i].split('/');
      var path = '';
      for (var j = 0; j < dirs.length; j++) {
        path = path + (path.length === 0 ? '' : '/') + dirs[j];
        if (newPaths.indexOf(path) === -1) {
          newPaths.push(path);
        }
      }
    }
    return newPaths;
  }

  var RULES = [
    // expected files
    {
      pattern: ['**/*'],
      options: {
        reportMissing: false,
        reportUnexpected: true,
        errorOnFailure: true,
        testFiles: true
      },
      expectations: [
        'res/lang/text.{en,en-gb}.properties',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8).properties',
        'res/lang/text.{en,en-gb}/*.text',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)/*.text',
        'res/lang/feature/*/text.*.properties',
        'res/lang/feature/*/text.*.properties.json',
        'res/quilts/*.quilt.xml',
        'res/quilts/custom/*.quilt.xml',
        'res/layouts/*.layout.xml',
        'res/grids/*.grid.xml',
        'res/orders/*.order.xml',
        'res/components/*.ftl',
        'res/endpoints/*.endpoint.xml',
        'res/controllers/*.ftl',
        'res/macros/*.ftl',
        'res/avatars/*/*/*.{png,gif,jpg,svg}',
        'res/init/Common.init.ftl',
        'res/skins/**/css/*.css',
        'res/skins/**/sass/**/*.{scss,sass}',
        'res/skins/**/sass/**/*.{scss,sass}.ftl',
        'res/skins/**/sass/**/*.scss.var',
        'res/skins/**/components/*.ftl',
        'res/skins/**/images/*.{png,gif,jpg,jpeg,svg}',
        'res/skins/**/skin.properties',
        'res/js/angularjs/**/*.js',
        'res/js/angularjs/metadata/dependencies.json',
        'res/editors/tinymce/**/*.js',
        'res/editors/tinymce/instances/**/*.json',
        'res/editors/tinymce/plugins/**/img/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/*/*/res/lang/text.{en,en-gb}.properties',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8).properties',
        'res/feature/*/*/res/lang/text.{en,en-gb}/*.text',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)/*.text',
        'res/feature/*/*/res/quilts/*.quilt.xml',
        'res/feature/*/*/res/layouts/*.layout.xml',
        'res/feature/*/*/res/grids/*.grid.xml',
        'res/feature/*/*/res/orders/*.order.xml',
        'res/feature/*/*/res/components/*.ftl',
        'res/feature/*/*/res/init/Common.init.ftl',
        'res/feature/*/*/res/skins/**/css/*.css',
        'res/feature/*/*/res/skins/**/sass/**/*.{scss,sass}',
        'res/feature/*/*/res/skins/**/sass/**/*.{scss,sass}.ftl',
        'res/feature/*/*/res/skins/**/sass/**/*.scss.var',
        'res/feature/*/*/res/skins/**/components/*.ftl',
        'res/feature/*/*/res/skins/**/images/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/*/*/res/skins/**/skin.properties',
        'res/feature/*/*/res/editors/tinymce/**/*.js',
        'res/feature/*/*/res/editors/tinymce/instances/**/*.json',
        'res/feature/*/*/res/editors/tinymce/plugins/**/img/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/feature-definition.xml',
        '{res,web}/**/README.md',
        '{res,web}/**/*.example',
        'web/html/**/*.*',
        'web/html/apps/**/*.*',
        'web/html/**/version'
      ]
    },
    // expected directories
    {
      pattern: ['**/*'],
      options: {
        reportMissing: false,
        reportUnexpected: true,
        errorOnFailure: true,
        testDirectories: true
      },
      expectations: allDirTreePaths([
        'res/editors/tinymce/**',
        'res/lang',
        'res/lang/text.{en,en-gb}',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,hr,hu,in,it,iw,ja,kk,ko,lv,ms,nl,no,pl,pt,pt-br,ro,ru,sk,sq,' +
          'sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)',
        'res/lang/feature/*',
        'res/quilts',
        'res/quilts/custom',
        'res/layouts',
        'res/grids',
        'res/orders',
        'res/components',
        'res/init',
        'res/controllers',
        'res/endpoints',
        'res/macros',
        'res/avatars/**/*',
        'res/skins/**',
        'res/js/angularjs/**',
        'res/js/angularjs/metadata',
        'res/feature/*/*/res/lang',
        'res/feature/*/*/res/lang/text.{en,en-gb}',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,hr,hu,in,it,iw,ja,kk,ko,lv,ms,nl,no,pl,pt,pt-br,ro,ru,sk,sq,' +
          'sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)',
        'res/feature/*/*/res/quilts',
        'res/feature/*/*/res/layouts',
        'res/feature/*/*/res/grids',
        'res/feature/*/*/res/orders',
        'res/feature/*/*/res/components',
        'res/feature/*/*/res/init',
        'res/feature/*/*/res/editors/tinymce/**',
        'res/feature/*/*/res/skins/**',
        'web/html/ng-app/status/version',
        'web/html/apps/**',
        'web/html/assets/**',
        'web/html/badge_icons/**',
        'web/html/rank_icons/**',
        'web/html/survey_icons/**',
        'web/html/**'
      ])
    },
    {
      pattern: '**/*',
      options: {
        reportMissing: true,
        reportUnexpected: false,
        errorOnFailure: true,
        testDirectories: true
      },
      expectations: allDirTreePaths([
        'res/**'
      ])
    }
  ];

  return {
    verify: function () {
      return streamSync(
        {
          collateErrors: true
        },
        RULES.map(function (rule) {
          return function () {

            return gulp.src(rule.pattern, { cwd: 'plugin', read: false })
              .pipe(through(function (file, enc, cb) {
                if (file.isStream()) {
                  cb(new gutil.PluginError('plugin-verify', 'Streaming not supported'));
                  return;
                }
                if (file.isNull() && file.stat && file.stat.isDirectory && file.stat.isDirectory()) {
                  if (rule.options.testDirectories) {
                    this.push(file);
                  }
                } else {
                  if (rule.options.testFiles) {
                    this.push(file);
                  }
                }
                cb();
              }))
              .pipe(expectFile(rule.options, rule.expectations));
          };
        })
      );
    }
  };
};
<commit_msg>added theme.xml<commit_after>'use strict';

var expectFile = require('gulp-expect-file');
var streamSync = require('./stream-sync');
var through = require('through2').obj;

module.exports = function (gulp, gutil) {

  function allDirTreePaths(paths) {
    var newPaths = [];
    for (var i = 0; i < paths.length; i++) {
      var dirs = paths[i].split('/');
      var path = '';
      for (var j = 0; j < dirs.length; j++) {
        path = path + (path.length === 0 ? '' : '/') + dirs[j];
        if (newPaths.indexOf(path) === -1) {
          newPaths.push(path);
        }
      }
    }
    return newPaths;
  }

  var RULES = [
    // expected files
    {
      pattern: ['**/*'],
      options: {
        reportMissing: false,
        reportUnexpected: true,
        errorOnFailure: true,
        testFiles: true
      },
      expectations: [
        'res/lang/text.{en,en-gb}.properties',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8).properties',
        'res/lang/text.{en,en-gb}/*.text',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)/*.text',
        'res/lang/feature/*/text.*.properties',
        'res/lang/feature/*/text.*.properties.json',
        'res/quilts/*.quilt.xml',
        'res/quilts/custom/*.quilt.xml',
        'res/layouts/*.layout.xml',
        'res/grids/*.grid.xml',
        'res/orders/*.order.xml',
        'res/components/*.ftl',
        'res/endpoints/*.endpoint.xml',
        'res/controllers/*.ftl',
        'res/macros/*.ftl',
        'res/avatars/*/*/*.{png,gif,jpg,svg}',
        'res/avatars/*.theme.xml',
        'res/init/Common.init.ftl',
        'res/skins/**/css/*.css',
        'res/skins/**/sass/**/*.{scss,sass}',
        'res/skins/**/sass/**/*.{scss,sass}.ftl',
        'res/skins/**/sass/**/*.scss.var',
        'res/skins/**/components/*.ftl',
        'res/skins/**/images/*.{png,gif,jpg,jpeg,svg}',
        'res/skins/**/skin.properties',
        'res/js/angularjs/**/*.js',
        'res/js/angularjs/metadata/dependencies.json',
        'res/editors/tinymce/**/*.js',
        'res/editors/tinymce/instances/**/*.json',
        'res/editors/tinymce/plugins/**/img/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/*/*/res/lang/text.{en,en-gb}.properties',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8).properties',
        'res/feature/*/*/res/lang/text.{en,en-gb}/*.text',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,he,hr,hu,id,in,it,iw,ja,kk,ko,lv,ms,nb,nl,no,' +
          'pl,pt,pt-br,pt-pt,ro,ru,sk,sq,sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)/*.text',
        'res/feature/*/*/res/quilts/*.quilt.xml',
        'res/feature/*/*/res/layouts/*.layout.xml',
        'res/feature/*/*/res/grids/*.grid.xml',
        'res/feature/*/*/res/orders/*.order.xml',
        'res/feature/*/*/res/components/*.ftl',
        'res/feature/*/*/res/init/Common.init.ftl',
        'res/feature/*/*/res/skins/**/css/*.css',
        'res/feature/*/*/res/skins/**/sass/**/*.{scss,sass}',
        'res/feature/*/*/res/skins/**/sass/**/*.{scss,sass}.ftl',
        'res/feature/*/*/res/skins/**/sass/**/*.scss.var',
        'res/feature/*/*/res/skins/**/components/*.ftl',
        'res/feature/*/*/res/skins/**/images/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/*/*/res/skins/**/skin.properties',
        'res/feature/*/*/res/editors/tinymce/**/*.js',
        'res/feature/*/*/res/editors/tinymce/instances/**/*.json',
        'res/feature/*/*/res/editors/tinymce/plugins/**/img/*.{png,gif,jpg,jpeg,svg}',
        'res/feature/feature-definition.xml',
        '{res,web}/**/README.md',
        '{res,web}/**/*.example',
        'web/html/**/*.*',
        'web/html/apps/**/*.*',
        'web/html/**/version'
      ]
    },
    // expected directories
    {
      pattern: ['**/*'],
      options: {
        reportMissing: false,
        reportUnexpected: true,
        errorOnFailure: true,
        testDirectories: true
      },
      expectations: allDirTreePaths([
        'res/editors/tinymce/**',
        'res/lang',
        'res/lang/text.{en,en-gb}',
        'res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,hr,hu,in,it,iw,ja,kk,ko,lv,ms,nl,no,pl,pt,pt-br,ro,ru,sk,sq,' +
          'sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)',
        'res/lang/feature/*',
        'res/quilts',
        'res/quilts/custom',
        'res/layouts',
        'res/grids',
        'res/orders',
        'res/components',
        'res/init',
        'res/controllers',
        'res/endpoints',
        'res/macros',
        'res/avatars/**/*',
        'res/skins/**',
        'res/js/angularjs/**',
        'res/js/angularjs/metadata',
        'res/feature/*/*/res/lang',
        'res/feature/*/*/res/lang/text.{en,en-gb}',
        'res/feature/*/*/res/lang/text.{ar,bg,ca,cs,da,de,el,es,es-mx,fi,fr,hr,hu,in,it,iw,ja,kk,ko,lv,ms,nl,no,pl,pt,pt-br,ro,ru,sk,sq,' +
          'sv,th,tr,uk,vi,zh-cn,zh-tw}.(UTF-8)',
        'res/feature/*/*/res/quilts',
        'res/feature/*/*/res/layouts',
        'res/feature/*/*/res/grids',
        'res/feature/*/*/res/orders',
        'res/feature/*/*/res/components',
        'res/feature/*/*/res/init',
        'res/feature/*/*/res/editors/tinymce/**',
        'res/feature/*/*/res/skins/**',
        'web/html/ng-app/status/version',
        'web/html/apps/**',
        'web/html/assets/**',
        'web/html/badge_icons/**',
        'web/html/rank_icons/**',
        'web/html/survey_icons/**',
        'web/html/**'
      ])
    },
    {
      pattern: '**/*',
      options: {
        reportMissing: true,
        reportUnexpected: false,
        errorOnFailure: true,
        testDirectories: true
      },
      expectations: allDirTreePaths([
        'res/**'
      ])
    }
  ];

  return {
    verify: function () {
      return streamSync(
        {
          collateErrors: true
        },
        RULES.map(function (rule) {
          return function () {

            return gulp.src(rule.pattern, { cwd: 'plugin', read: false })
              .pipe(through(function (file, enc, cb) {
                if (file.isStream()) {
                  cb(new gutil.PluginError('plugin-verify', 'Streaming not supported'));
                  return;
                }
                if (file.isNull() && file.stat && file.stat.isDirectory && file.stat.isDirectory()) {
                  if (rule.options.testDirectories) {
                    this.push(file);
                  }
                } else {
                  if (rule.options.testFiles) {
                    this.push(file);
                  }
                }
                cb();
              }))
              .pipe(expectFile(rule.options, rule.expectations));
          };
        })
      );
    }
  };
};
<|endoftext|>


<commit_before>
var Bind = module.exports = function (bind, dataContext) {

    var self = this;

    var context = $(this.dom);
    if (bind.context) {
        context = $(bind.context, context);
    }
    var target = context;
    if (bind.target) {
        target = $(bind.target, context);
    }

    dataContext = dataContext || {};

    function computeStringOrSourceDataValue(dataType, dataContext) {
        var value = "";
        if (typeof dataType === "string") {
            value = dataType;
        } else {
            value = computeDataValue(dataType, dataContext);
        }
        return value;
    }

    function computeDataValue(dataType, dataContext) {

        var dataSource = undefined;

        if (dataType.source === "$") {
            dataSource = dataContext.toString();
        } else {
            dataSource = dataContext[dataType.source];
        }

        if (dataSource === undefined || dataSource === null) {
            value = "?" + dataType.source + "?";
        } else if (typeof dataSource === "object") {
            var locale = M.getLocale();
            value = dataSource[locale] || "Missing value for '" + locale + "' language";
        } else {
            value = dataSource.toString();
        }

        var filterFunction = self[dataType.filter] || window[dataType.filter];
        if (typeof filterFunction === "function") {
            value = filterFunction(self, dataContext, dataType.source, value);
        }

        return value;
    };

    var domManipulators = {

        // add/modify attribute
        /* Examples:
         *  "binds": [
         *      {
         *          "attr": [
         *              {
         *                  "name": "the_attribute name",
         *                  "value": "a_string_value"
         *              },
         *              {
         *                  "name": "the_attribute name",
         *                  "value": {
         *                      "source": "the_field_name_from_a_source"
         *                  }
         *              },
         *              ...
         *          ]
         *      }
         *  ]
         */
        attr: function(target, context, attrTypes, dataContext) {
            for (var i in attrTypes) {
                var value = computeStringOrSourceDataValue(attrTypes[i].value, dataContext);
                target.attr(attrTypes[i].name, value);
            }
        },

        // add class
        /* Examples:
         *  "binds": [
         *      {
         *          "addClass": [
         *              "class_1",
         *              "class_2",
         *              ...
         *          ]
         *      }
         *  ]
         */
        addClass: function(target, context, classes, dataContext) {
            for (var i in classes) {
                target.addClass(classes[i]);
            }
        },

        // remove class
        /* Examples:
         *  "binds": [
         *      {
         *          "removeClass": [
         *              "class_1",
         *              "class_2",
         *              ...
         *          ]
         *      }
         *  ]
         */
        removeClass: function(target, context, classes, dataContext) {
            for (var i in classes) {
                target.removeClass(classes[i]);
            }
        },

        // innerHTML
        /* Examples:
         *  "binds": [
         *      {
         *          "html": "a_string_value"
         *      },
         *
         *      {
         *          "html": {
         *              "source": "the_field_name_from_a_source"
         *          }
         *      }
         *  ]
         */
        html: function(target, context, dataType, dataContext) {
            var value = computeStringOrSourceDataValue(dataType, dataContext);
            target.html(value);
        },

        // repeat the target for each item in array source
        /*
         *   "binds": [
         *       {
         *           "repeat": {
         *               "source": "the_array_name_from_a_source",
         *               "preFilter": "handler_name",
         *               "postFilter": "handler_name",
         *               "binds": [
         *                   ...
         *               ]
         *           }
         *       }
         *   ]
         */
        repeat: function(target, context, bindTemplate, dataContext) {

            if (!target.length) {
                console.error("Empty bind 'repeat' target");
                return;
            }

            if (typeof dataContext !== "object" || !dataContext) {
                console.error("A bind 'repeat' can only be used with an object data context");
                return;
            }

            var sourceArray = dataContext[bindTemplate.source];

            if (Object.prototype.toString.call(sourceArray) !== "[object Array]" ) {
                console.error("A bind 'repeat' did not find an array in the source field of the data context");
                return;
            }

            var template = target.clone();
            var container = target.parent();

            // run the pre-filtering handler
            var filterFunction = self[bindTemplate.preFilter] || window[bindTemplate.prefilter];
            if (typeof filterFunction === "function") {
                sourceArray = filterFunction(self, dataContext, bindTemplate.source, sourceArray, container);
            }

            // run the binds for each item
            for (var i = 0; i < sourceArray.length; ++i) {

                var newDom = template.clone();

                if (!bindTemplate.binds || !bindTemplate.binds.length) {
                    continue;
                }

                for (var j = 0; j < bindTemplate.binds.length; ++j) {
                    var bind = bindTemplate.binds[j];
                    bind.context = newDom;
                    Bind.call(self, bind, sourceArray[i]);
                }

                container.append(newDom);
            }

            // run the post-filtering handler
            filterFunction = self[bindTemplate.postFilter] || window[bindTemplate.postFilter];
            if (typeof filterFunction === "function") {
                sourceArray = filterFunction(self, dataContext, bindTemplate.source, sourceArray, container);
            }

            target.remove();
        }
    }

    for (var i in bind) {
        if (domManipulators[i]) {
            domManipulators[i](target, context, bind[i], dataContext);
        }
    }

    for (var i in bind.on) {
        var curOn = bind.on[i];
        if (curOn.handler || curOn.emit) {
            target.on(curOn.name, function(event) {
                event.stopPropagation();

                var handler = self[curOn.handler];
                if (typeof handler === "function") {
                    handler(dataContext);
                }
                if (curOn.emit) {
                    self.emit(curOn.emit, dataContext);
                }

                return false;
            });
        }
    }

    for (var i in bind.listen) {
        var curListen = bind.listen[i];
        self.on(curListen.name, curListen.miid, self[curListen.handler]);
    }
};

<commit_msg>Fixed custom filtering function references; they need a mongo-style dot notation navigation into objects<commit_after>
function findFunction  (parent, dotNot) {
    if (!dotNot) return undefined;
    var splits = dotNot.split('.');
    var func;
    for (var i = 0; i < splits.length; i++) {
        func = parent[splits[i]];
        if (!func) return undefined;
        if (typeof func === 'object') parent = func;
    }
    if (typeof func !== 'function') {
        return undefined;
    }

    return func;
}

var Bind = module.exports = function (bind, dataContext) {

    var self = this;

    var context = $(this.dom);
    if (bind.context) {
        context = $(bind.context, context);
    }
    var target = context;
    if (bind.target) {
        target = $(bind.target, context);
    }

    dataContext = dataContext || {};

    function computeStringOrSourceDataValue(dataType, dataContext) {
        var value = "";
        if (typeof dataType === "string") {
            value = dataType;
        } else {
            value = computeDataValue(dataType, dataContext);
        }
        return value;
    }

    function computeDataValue(dataType, dataContext) {

        var dataSource = undefined;

        if (dataType.source === "$") {
            dataSource = dataContext.toString();
        } else {
            dataSource = dataContext[dataType.source];
        }

        if (dataSource === undefined || dataSource === null) {
            value = "?" + dataType.source + "?";
        } else if (typeof dataSource === "object") {
            var locale = M.getLocale();
            value = dataSource[locale] || "Missing value for '" + locale + "' language";
        } else {
            value = dataSource.toString();
        }

        var filterFunction = findFunction(self, dataType.filter) || findFunction(window, dataType.filter);
        if (typeof filterFunction === "function") {
            value = filterFunction(self, dataContext, dataType.source, value);
        }

        return value;
    };

    var domManipulators = {

        // add/modify attribute
        /* Examples:
         *  "binds": [
         *      {
         *          "attr": [
         *              {
         *                  "name": "the_attribute name",
         *                  "value": "a_string_value"
         *              },
         *              {
         *                  "name": "the_attribute name",
         *                  "value": {
         *                      "source": "the_field_name_from_a_source"
         *                  }
         *              },
         *              ...
         *          ]
         *      }
         *  ]
         */
        attr: function(target, context, attrTypes, dataContext) {
            for (var i in attrTypes) {
                var value = computeStringOrSourceDataValue(attrTypes[i].value, dataContext);
                target.attr(attrTypes[i].name, value);
            }
        },

        // add class
        /* Examples:
         *  "binds": [
         *      {
         *          "addClass": [
         *              "class_1",
         *              "class_2",
         *              ...
         *          ]
         *      }
         *  ]
         */
        addClass: function(target, context, classes, dataContext) {
            for (var i in classes) {
                target.addClass(classes[i]);
            }
        },

        // remove class
        /* Examples:
         *  "binds": [
         *      {
         *          "removeClass": [
         *              "class_1",
         *              "class_2",
         *              ...
         *          ]
         *      }
         *  ]
         */
        removeClass: function(target, context, classes, dataContext) {
            for (var i in classes) {
                target.removeClass(classes[i]);
            }
        },

        // innerHTML
        /* Examples:
         *  "binds": [
         *      {
         *          "html": "a_string_value"
         *      },
         *
         *      {
         *          "html": {
         *              "source": "the_field_name_from_a_source"
         *          }
         *      }
         *  ]
         */
        html: function(target, context, dataType, dataContext) {
            var value = computeStringOrSourceDataValue(dataType, dataContext);
            target.html(value);
        },

        // repeat the target for each item in array source
        /*
         *   "binds": [
         *       {
         *           "repeat": {
         *               "source": "the_array_name_from_a_source",
         *               "preFilter": "handler_name",
         *               "postFilter": "handler_name",
         *               "binds": [
         *                   ...
         *               ]
         *           }
         *       }
         *   ]
         */
        repeat: function(target, context, bindTemplate, dataContext) {

            if (!target.length) {
                console.warn("Empty bind 'repeat' target");
                return;
            }

            if (typeof dataContext !== "object" || !dataContext) {
                console.warn("A bind 'repeat' can only be used with an object data context");
                return;
            }

            var sourceArray = dataContext[bindTemplate.source] || [];

            if (Object.prototype.toString.call(sourceArray) !== "[object Array]" ) {
                console.warn("A bind 'repeat' did not find an array in the source field of the data context");
                return;
            }

            var template = target.clone();
            var container = target.parent();

            // run the pre-filtering handler
            var filterFunction = findFunction(self, bindTemplate.preFilter) || findFunction(window, bindTemplate.prefilter);
            if (filterFunction) {
                sourceArray = filterFunction(self, dataContext, bindTemplate.source, sourceArray, container);
            }

            // run the binds for each item
            for (var i = 0; i < sourceArray.length; ++i) {

                var newDom = template.clone();

                if (!bindTemplate.binds || !bindTemplate.binds.length) {
                    continue;
                }

                for (var j = 0; j < bindTemplate.binds.length; ++j) {
                    var bind = bindTemplate.binds[j];
                    bind.context = newDom;
                    Bind.call(self, bind, sourceArray[i]);
                }

                container.append(newDom);
            }

            // run the post-filtering handler
            filterFunction = findFunction(self, bindTemplate.postFilter) || findFunction(window, bindTemplate.postFilter);
            if (filterFunction) {
                sourceArray = filterFunction(self, dataContext, bindTemplate.source, sourceArray, container);
            }

            target.remove();
        }
    }

    for (var i in bind) {
        if (domManipulators[i]) {
            domManipulators[i](target, context, bind[i], dataContext);
        }
    }

    for (var i in bind.on) {
        var curOn = bind.on[i];
        if (curOn.handler || curOn.emit) {
            target.on(curOn.name, function(event) {
                event.stopPropagation();

                var handler = self[curOn.handler];
                if (typeof handler === "function") {
                    handler(dataContext);
                }
                if (curOn.emit) {
                    self.emit(curOn.emit, dataContext);
                }

                return false;
            });
        }
    }

    for (var i in bind.listen) {
        var curListen = bind.listen[i];
        self.on(curListen.name, curListen.miid, self[curListen.handler]);
    }
};

<|endoftext|>


<commit_before>var hidden_messages = {};
var client_messages_frame_id;

function set_client_messages_frame_id(id) {
    client_messages_frame_id = id;
    $(id+"_hide").onclick = function() {
        toggle_client_messages_frame();
    }
    $(id+"_show_hidden").onclick = function() {
        show_all_client_messages();
    }
    client_messages_add_discard_buttons();
    show_client_messages();
}

function client_messages_add_discard_buttons() {
    var messages_frame = $(client_messages_frame_id + "_content");
    var childs = messages_frame.childElements();
    for(var i = 0; i < childs.length; i++) {
        if(!childs[i].hasClassName("popup_notice"))
            continue;
        var single_messages = childs[i].childElements();
        for(var j = 0; j < single_messages.length; j++) {
            var div = document.createElement("div");
            div.innerHTML = "discard";
            div.addClassName("popup_notice_frame_close_button");
            div.onclick = function() {
                hide_client_message(this.parentNode);
            }
            single_messages[j].appendChild(div)
        }
    }

}

function toggle_client_messages_frame() {
    if(!client_messages_frame_id)
        return;
    var obj = $(client_messages_frame_id + "_content");
    if(obj.visible()) {
        obj.hide();
        $(client_messages_frame_id+"_hide").innerHTML = "Show messages";
    } else {
        obj.show();
        $(client_messages_frame_id+"_hide").innerHTML = "Hide messages";
    }
    show_client_messages();
}

function hide_client_message(object) {
    if(!object)
        return;
    hidden_messages[object.innerHTML] = 1;
    show_client_messages();
}

function show_all_client_messages() {
    hidden_messages = {};
    show_client_messages();
}

function show_client_messages() {
    if(!client_messages_frame_id)
        return;
    var messages_frame = $(client_messages_frame_id + "_content");
    var childs = messages_frame.childElements();
    // hide buttons
    $(client_messages_frame_id+"_hide").hide();
    $(client_messages_frame_id+"_show_hidden").hide();
    if(!childs.length)
        return;

    var hidden = 0;
    var total_displayed = 0;
    for(var i = 0; i < childs.length; i++) {
        if(!childs[i].hasClassName("popup_notice"))
            continue;
        var single_messages = childs[i].childElements();
        var total = single_messages.length;
        var displayed = 0;
        for(var j = 0; j < total; j++) {
            if(hidden_messages[single_messages[j].innerHTML]) {
                single_messages[j].hide();
                hidden++;
            } else {
                single_messages[j].show();
                displayed++;
                total_displayed++;
            }
        }
        if(!displayed)
            childs[i].hide();
        else
            childs[i].show();
    }
    if(total_displayed)
        $(client_messages_frame_id+"_hide").show();

    if(messages_frame.visible() && hidden)
        $(client_messages_frame_id+"_show_hidden").show();

}<commit_msg>discarding client messages - fix bugs<commit_after>var discarded_messages = {};
var client_messages_frame_id;

function set_client_messages_frame_id(id) {
    client_messages_frame_id = id;
    $(id+"_hide").onclick = function() {
        toggle_client_messages_frame();
    }
    $(id+"_show_discarded").onclick = function() {
        show_all_client_messages();
    }
    client_messages_add_discard_buttons();
    show_client_messages();
}

function client_messages_add_discard_buttons() {
    var messages_frame = $(client_messages_frame_id + "_content");
    var childs = messages_frame.childElements();
    for(var i = 0; i < childs.length; i++) {
        if(!childs[i].hasClassName("popup_notice"))
            continue;
        var single_messages = childs[i].childElements();
        for(var j = 0; j < single_messages.length; j++) {
            var div = document.createElement("div");
            div.innerHTML = "discard";
            div.addClassName("popup_notice_frame_close_button");
            div.onclick = function() {
                hide_client_message(this.parentNode);
            }
            single_messages[j].appendChild(div)
        }
    }

}

function toggle_client_messages_frame() {
    if(!client_messages_frame_id)
        return;
    var obj = $(client_messages_frame_id + "_content");
    if(obj.visible()) {
        obj.hide();
        $(client_messages_frame_id+"_hide").innerHTML = "Show messages";
    } else {
        obj.show();
        $(client_messages_frame_id+"_hide").innerHTML = "Hide messages";
    }
    show_client_messages();
}

function hide_client_message(object) {
    if(!object)
        return;
    discarded_messages[object.innerHTML] = 1;
    show_client_messages();
}

function show_all_client_messages() {
    discarded_messages = {};
    show_client_messages();
}

function show_client_messages() {
    if(!client_messages_frame_id)
        return;
    var messages_frame = $(client_messages_frame_id + "_content");
    var childs = messages_frame.childElements();
    // hide buttons
    $(client_messages_frame_id+"_hide").hide();
    $(client_messages_frame_id+"_show_discarded").hide();
    if(!childs.length)
        return;

    var discarded = 0;
    var total_displayed = 0;
    for(var i = 0; i < childs.length; i++) {
        if(!childs[i].hasClassName("popup_notice"))
            continue;
        var single_messages = childs[i].childElements();
        var total = single_messages.length;
        var displayed = 0;
        for(var j = 0; j < total; j++) {
            if(discarded_messages[single_messages[j].innerHTML]) {
                single_messages[j].hide();
                discarded++;
            } else {
                single_messages[j].show();
                displayed++;
                total_displayed++;
            }
        }
        if(!displayed)
            childs[i].hide();
        else
            childs[i].show();
    }
    if(total_displayed)
        $(client_messages_frame_id+"_hide").show();

    if(messages_frame.visible() && discarded)
        $(client_messages_frame_id+"_show_discarded").show();

}<|endoftext|>


<commit_before>// Happen as soon as the DOM is loaded and before assets are downloaded
jQuery(function($) {
  $('.js-hide-other-links').hideOtherLinks();
  $('.js-hide-other-departments').hideOtherLinks({ linkElement: 'span', alwaysVisibleClass: '.lead' });

  $('.govspeak').enhanceYoutubeVideoLinks();

  $('.detailed-guides-show').trackExternalLinks();

  GOVUK.stickAtTopWhenScrolling.init();

  $('.js-toggle-change-notes').toggler({actLikeLightbox: true});
  $('.js-toggle-change-notes').click(function(){
    if(!window.changeNoteTrackingEventSent){
      window._gaq && _gaq.push(['_trackEvent', 'edd_inside_gov', 'change_note', 'shown', 0, true]);
      window.changeNoteTrackingEventSent = true;
    }
  });
  $('.js-toggle-footer-change-notes').toggler();

  $('.js-toggle-accessibility-warning').toggler({header: ".toggler", content: ".help-block"})
  $('.js-toggle-org-list').toggler({actLikeLightbox: true})

  $(".js-document-filter").enableDocumentFilter();

  $('.js-hide-extra-logos .organisations-icon-list').hideExtraRows({
    appendToParent: true,
    showWrapper: $('<li/>')
  });
  $('.js-hide-extra-contacts').hideExtraRows({ rows: 2 });
  $('.js-hide-extra-social-media').hideExtraRows({ rows: 5 });

  GOVUK.hideDepartmentChildren.init();
  GOVUK.filterListItems.init();
  GOVUK.showHide.init();
  GOVUK.virtualTour.init();
  GOVUK.feeds.init();

});
// These want images to be loaded before they run so the page height doesn't change.
jQuery(window).load(function(){
  GOVUK.backToContent.init();
});
<commit_msg>Remove tracking from change notes<commit_after>// Happen as soon as the DOM is loaded and before assets are downloaded
jQuery(function($) {
  $('.js-hide-other-links').hideOtherLinks();
  $('.js-hide-other-departments').hideOtherLinks({ linkElement: 'span', alwaysVisibleClass: '.lead' });

  $('.govspeak').enhanceYoutubeVideoLinks();

  $('.detailed-guides-show').trackExternalLinks();

  GOVUK.stickAtTopWhenScrolling.init();

  $('.js-toggle-change-notes').toggler({actLikeLightbox: true});
  $('.js-toggle-footer-change-notes').toggler();

  $('.js-toggle-accessibility-warning').toggler({header: ".toggler", content: ".help-block"})
  $('.js-toggle-org-list').toggler({actLikeLightbox: true})

  $(".js-document-filter").enableDocumentFilter();

  $('.js-hide-extra-logos .organisations-icon-list').hideExtraRows({
    appendToParent: true,
    showWrapper: $('<li/>')
  });
  $('.js-hide-extra-contacts').hideExtraRows({ rows: 2 });
  $('.js-hide-extra-social-media').hideExtraRows({ rows: 5 });

  GOVUK.hideDepartmentChildren.init();
  GOVUK.filterListItems.init();
  GOVUK.showHide.init();
  GOVUK.virtualTour.init();
  GOVUK.feeds.init();

});
// These want images to be loaded before they run so the page height doesn't change.
jQuery(window).load(function(){
  GOVUK.backToContent.init();
});
<|endoftext|>


<commit_before>!function($) {

  "use strict"; // jshint ;_;

  /* MODAL_FORM PUBLIC CLASS DEFINITION
   * =============================== */

  var ModalForm = function ( element, options, trigger ) {

    this.options = options;
    this.$element = $(element);
    this.$trigger = $(trigger);

    this.init();
  }

  /* NOTE: MODAL_FORM EXTENDS BOOTSTRAP-MODAL.js
   * ========================================== */

  ModalForm.prototype = new $.fn.modal.Constructor();

  $.extend(ModalForm.prototype, {

    init: function() {
      var that = this;
      var $form;
      this.$element
        .on('keypress', 'form', $.proxy(this.keypress_submit, this))
        .on('keyup', 'form', $.proxy(this.keyup_escape, this))
        .on('click.modal-form.close', '[data-dismiss="modal"]', $.proxy(this.hide, this))
        .on('click.modal-form.reset', 'input[type=reset], [data-dismiss="modal-reset"]', $.proxy(this.reset, this))
        .on('click.modal-form.submit', 'input[type=submit], [data-toggle="modal-submit"]', $.proxy(this.submit, this))
        .on('shown.modal-form', $.proxy(this.focus_first_input, this))
        .on('loaded.modal-form', $.proxy(this.focus_first_input, this))
        .on('loaded.modal-form', function(ev) {
          $("a[data-wysihtml5-command], a[data-wysihtml5-action]", ev.target).attr('tabindex', "-1");
          $form = that.$form();
        })
        .on('delete-object', $.proxy(this.delete_object, this))
        ;

      $(document).on("ajax:complete", function(ev, xhr2) {
        if(xhr2 === that.xhr) {
          delete that.xhr;
          $("[data-toggle=modal-submit]", $form).removeAttr("disabled").removeClass("disabled pending-ajax").each(function() {  $(this).text($(this).data("origText")); });
          $form.data("submitpending", false);
        }
      });

    }

  , doNothing: function(e) {
    e.stopImmediatePropagation();
    e.stopPropagation();
    e.preventDefault();
  }

  , delete_object: function(e, data, xhr) {
      // If this modal is contained within another modal, pass the event onward
      var $trigger_modal = this.$trigger.closest('.modal')
        , delete_target
        ;

      if ($trigger_modal.length > 0) {
        $trigger_modal.trigger('delete-object', [data, xhr]);
      } else {
        delete_target = this.$trigger.data('delete-target');
        if (delete_target == 'refresh') {
          // Refresh the page
          window.location.assign(window.location.href.replace(/#.*/, ''));
        } else if (xhr && xhr.getResponseHeader('location')) {
          // Otherwise redirect if possible
          window.location.assign(xhr.getResponseHeader('location'));
        } else {
          // Otherwise refresh the page
          window.location.assign(window.location.href.replace(/#.*/, ''));
        }
      }
    }

  , $form: function() {
      return this.$element.find('form').first();
    }

  , submit: function(e) {
      var $form = this.$form()
      , that = this;

      if(!$form.data("submitpending")) {
        $("[data-toggle=modal-submit]", $form)
          .each(function() { $(this).data("origText", $(this).text()); })
          .addClass("disabled pending-ajax")
          .attr("disabled", true);

        $form.data("submitpending", true)
        .one("ajax:beforeSend", function(ev, _xhr){
          that.xhr = _xhr;
        })
        .submit();
      }
      if (e.type == 'click')
        e.preventDefault();
    }

  , keypress_submit: function(e) {
      if (e.which == 13 && !$(e.target).is('textarea')) {
        if (!e.isDefaultPrevented()) {
          e.preventDefault();
          this.$form().submit();
        }
      }
    }

  , keyup_escape : function(e) {
     if($(document.activeElement).is("select, [data-toggle=datepicker]") && e.which === 27) {

        this.$element.attr("tabindex", -1).focus();
        e.stopPropagation();

      }
    }

  , reset: function(e) {
      var form = this.$form()[0];
      form && form.reset();
      this.hide(e);
    }

  , hide: function(e) {
      $.fn.modal.Constructor.prototype.hide.apply(this, [e]);
      this.$element.off('modal_form');
    }

  , focus_first_input: function(ev) {
      var $first_input = this.$element
        .find('input[type="text"], input[type="checkbox"], select, textarea')
        .not('[placeholder*=autofill], label:contains(autofill) + *, [disabled]')
        .first();
      if ($first_input.length > 0 && (!ev || this.$element.is(ev.target)))
        setTimeout(function() { $first_input.get(0).focus(); }, 100);
    }
  });

  $.fn.modal_form = function(option, trigger) {
    return this.each(function() {
      var $this = $(this)
        , data = $this.data('modal_form')
        , options = $.extend({}, $.fn.modal_form.defaults, $this.data(), typeof option == 'object' && option);
      if (!data) $this.data('modal_form', (data = new ModalForm(this, options, trigger)));
      if (typeof option == 'string') data[option]();
      else if (options.show) data.show();
    });
  }

  $.fn.modal_form.Constructor = ModalForm;

  $.fn.modal_form.defaults = $.extend({}, $.fn.modal.defaults, {

  });

  /* MODAL-FORM DATA-API
   * =================== */

  $(function () {
    $('body').on('click.modal-form.data-api', '[data-toggle="modal-form"]', function ( e ) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , option = $target.data('modal-form') ? 'toggle' : $.extend({}, $target.data(), $this.data());

      e.preventDefault();
      $target.modal_form(option);
    });
  });

  // Default flash handler
  $(function() {
    // Default form complete handler
    $('body').on('ajax:complete', function(e, xhr, status) {
      var data = null, data_k;
      try {
        data = JSON.parse(xhr.responseText);
      } catch (exc) {
        //console.debug('exc', exc);
      }

      if (data) {
        // Parse and dispatch JSON object
        $(e.target).trigger('ajax:json', [data, xhr]);
      } else {
        // Dispatch as html
        $(e.target).trigger('ajax:html', [xhr.responseText, xhr]);
      }

      if (!e.stopRedirect) {
        // Maybe handle AJAX/JSON redirect or refresh
        if (xhr.status == 278) {
          // Handle 278 redirect (AJAX redirect)
          window.location.assign(xhr.getResponseHeader('location'));
        } else if (xhr.status == 279) {
          // Handle 279 page refresh
          window.location.assign(window.location.href.replace(/#.*/, ''));
        }
      }

      if (!e.stopFlash) {
        // Maybe handle AJAX flash messages
        var flash_types = ["error", "alert", "notice", "warning"]
          , type_i, message
          , flash;

        for (type_i in flash_types) {
          message = xhr.getResponseHeader('x-flash-' + flash_types[type_i]);
          message = JSON.parse(message);
          if (message) {
            if (!flash)
              flash = {};
            flash[flash_types[type_i]] = message;
          }
        }

        if (flash) {
          $(e.target).trigger('ajax:flash', flash);
        }
      }
    });

    $('body').on('ajax:flash', function(e, flash) {
      var $target, $flash_holder// = this.$element.find('.flash')
        , type, ucase_type
        , messages, message, message_i
        , flash_class
        , flash_class_mappings = { notice: "success" }
        , html;

      // Find or create the flash-message holder
      $target = e.target ? $(e.target) : $('body');
      $flash_holder = $target.find('.flash');

      if ($flash_holder.length == 0) {
        $flash_holder = $('

');
        $target.find('.modal-body').prepend($flash_holder);
      } else {
        $flash_holder.empty();
      }

      for (type in flash) {
        if (flash[type]) {
          if (typeof(flash[type]) == "string")
            flash[type] = [flash[type]];

          flash_class = flash_class_mappings[type] || type

          html =
            [ '
'
            ,   'x'
            ]
          for (message_i in flash[type]) {
            html.push('' + flash[type][message_i] + '');
          }
          html.push('
');
          $flash_holder.append(html.join(''));
        }
      }
      //e.stopPropagation();
    });

    $('body').on('ajax:html', '.modal > form', function(e, html, xhr) {
      $(this).find('.modal-body').html(html);
    });

    //$('body').on('ajax:json', function(e, data, xhr) {
    //});
  });
}(window.jQuery);
<commit_msg>Story 43130865: Refactored modal form ajax:complete handling s.t. submit buttons are only re-enabled when a refresh of the page is not pending.<commit_after>!function($) {

  "use strict"; // jshint ;_;

  /* MODAL_FORM PUBLIC CLASS DEFINITION
   * =============================== */

  var ModalForm = function ( element, options, trigger ) {

    this.options = options;
    this.$element = $(element);
    this.$trigger = $(trigger);

    this.init();
  }

  /* NOTE: MODAL_FORM EXTENDS BOOTSTRAP-MODAL.js
   * ========================================== */

  ModalForm.prototype = new $.fn.modal.Constructor();

  $.extend(ModalForm.prototype, {

    init: function() {
      var that = this;
      var $form;
      this.$element
        .on('keypress', 'form', $.proxy(this.keypress_submit, this))
        .on('keyup', 'form', $.proxy(this.keyup_escape, this))
        .on('click.modal-form.close', '[data-dismiss="modal"]', $.proxy(this.hide, this))
        .on('click.modal-form.reset', 'input[type=reset], [data-dismiss="modal-reset"]', $.proxy(this.reset, this))
        .on('click.modal-form.submit', 'input[type=submit], [data-toggle="modal-submit"]', $.proxy(this.submit, this))
        .on('shown.modal-form', $.proxy(this.focus_first_input, this))
        .on('loaded.modal-form', $.proxy(this.focus_first_input, this))
        .on('loaded.modal-form', function(ev) {
          $("a[data-wysihtml5-command], a[data-wysihtml5-action]", ev.target).attr('tabindex', "-1");
          $form = that.$form();
        })
        .on('delete-object', $.proxy(this.delete_object, this))
        ;

    }

  , doNothing: function(e) {
    e.stopImmediatePropagation();
    e.stopPropagation();
    e.preventDefault();
  }

  , delete_object: function(e, data, xhr) {
      // If this modal is contained within another modal, pass the event onward
      var $trigger_modal = this.$trigger.closest('.modal')
        , delete_target
        ;

      if ($trigger_modal.length > 0) {
        $trigger_modal.trigger('delete-object', [data, xhr]);
      } else {
        delete_target = this.$trigger.data('delete-target');
        if (delete_target == 'refresh') {
          // Refresh the page
          window.location.assign(window.location.href.replace(/#.*/, ''));
        } else if (xhr && xhr.getResponseHeader('location')) {
          // Otherwise redirect if possible
          window.location.assign(xhr.getResponseHeader('location'));
        } else {
          // Otherwise refresh the page
          window.location.assign(window.location.href.replace(/#.*/, ''));
        }
      }
    }

  , $form: function() {
      return this.$element.find('form').first();
    }

  , submit: function(e) {
      var $form = this.$form()
      , that = this;

      if(!$form.data("submitpending")) {
        $("[data-toggle=modal-submit]", $form)
          .each(function() { $(this).data("origText", $(this).text()); })
          .addClass("disabled pending-ajax")
          .attr("disabled", true);

        $form.data("submitpending", true)
        .one("ajax:beforeSend", function(ev, _xhr){
          that.xhr = _xhr;
        })
        .submit();
      }
      if (e.type == 'click')
        e.preventDefault();
    }

  , keypress_submit: function(e) {
      if (e.which == 13 && !$(e.target).is('textarea')) {
        if (!e.isDefaultPrevented()) {
          e.preventDefault();
          this.$form().submit();
        }
      }
    }

  , keyup_escape : function(e) {
     if($(document.activeElement).is("select, [data-toggle=datepicker]") && e.which === 27) {

        this.$element.attr("tabindex", -1).focus();
        e.stopPropagation();

      }
    }

  , reset: function(e) {
      var form = this.$form()[0];
      form && form.reset();
      this.hide(e);
    }

  , hide: function(e) {
      $.fn.modal.Constructor.prototype.hide.apply(this, [e]);
      this.$element.off('modal_form');
    }

  , focus_first_input: function(ev) {
      var $first_input = this.$element
        .find('input[type="text"], input[type="checkbox"], select, textarea')
        .not('[placeholder*=autofill], label:contains(autofill) + *, [disabled]')
        .first();
      if ($first_input.length > 0 && (!ev || this.$element.is(ev.target)))
        setTimeout(function() { $first_input.get(0).focus(); }, 100);
    }
  });

  $.fn.modal_form = function(option, trigger) {
    return this.each(function() {
      var $this = $(this)
        , data = $this.data('modal_form')
        , options = $.extend({}, $.fn.modal_form.defaults, $this.data(), typeof option == 'object' && option);
      if (!data) $this.data('modal_form', (data = new ModalForm(this, options, trigger)));
      if (typeof option == 'string') data[option]();
      else if (options.show) data.show();
    });
  }

  $.fn.modal_form.Constructor = ModalForm;

  $.fn.modal_form.defaults = $.extend({}, $.fn.modal.defaults, {

  });

  /* MODAL-FORM DATA-API
   * =================== */

  $(function () {
    $('body').on('click.modal-form.data-api', '[data-toggle="modal-form"]', function ( e ) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , option = $target.data('modal-form') ? 'toggle' : $.extend({}, $target.data(), $this.data());

      e.preventDefault();
      $target.modal_form(option);
    });
  });

  // Default flash handler
  $(function() {
    // Default form complete handler
    $('body').on('ajax:complete', function(e, xhr, status) {
      var data = null, data_k;
      try {
        data = JSON.parse(xhr.responseText);
      } catch (exc) {
        //console.debug('exc', exc);
      }

      if (data) {
        // Parse and dispatch JSON object
        $(e.target).trigger('ajax:json', [data, xhr]);
      } else if(xhr.responseText) {
        // Dispatch as html, if there is html to dispatch.  (no result should not blank out forms)
        $(e.target).trigger('ajax:html', [xhr.responseText, xhr]);
      }

      if (!e.stopRedirect) {
        // Maybe handle AJAX/JSON redirect or refresh
        if (xhr.status == 278) {
          // Handle 278 redirect (AJAX redirect)
          window.location.assign(xhr.getResponseHeader('location'));
        } else if (xhr.status == 279) {
          // Handle 279 page refresh
          window.location.assign(window.location.href.replace(/#.*/, ''));
        }
        else {
          var modal_form = $(".modal:visible").data("modal_form");
          if(xhr === modal_form.xhr) {
            delete modal_form.xhr;
            $("[data-toggle=modal-submit]", modal_form.$element)
            .removeAttr("disabled")
            .removeClass("disabled pending-ajax")
            .each(function() {
              $(this).text($(this).data("origText"));
            });
            $("form", modal_form.$element).data("submitpending", false);
          }
        }
      }

      if (!e.stopFlash) {
        // Maybe handle AJAX flash messages
        var flash_types = ["error", "alert", "notice", "warning"]
          , type_i, message
          , flash;

        for (type_i in flash_types) {
          message = xhr.getResponseHeader('x-flash-' + flash_types[type_i]);
          message = JSON.parse(message);
          if (message) {
            if (!flash)
              flash = {};
            flash[flash_types[type_i]] = message;
          }
        }

        if (flash) {
          $(e.target).trigger('ajax:flash', flash);
        }
      }
    });

    $('body').on('ajax:flash', function(e, flash) {
      var $target, $flash_holder// = this.$element.find('.flash')
        , type, ucase_type
        , messages, message, message_i
        , flash_class
        , flash_class_mappings = { notice: "success" }
        , html;

      // Find or create the flash-message holder
      $target = e.target ? $(e.target) : $('body');
      $flash_holder = $target.find('.flash');

      if ($flash_holder.length == 0) {
        $flash_holder = $('

');
        $target.find('.modal-body').prepend($flash_holder);
      } else {
        $flash_holder.empty();
      }

      for (type in flash) {
        if (flash[type]) {
          if (typeof(flash[type]) == "string")
            flash[type] = [flash[type]];

          flash_class = flash_class_mappings[type] || type

          html =
            [ '
'
            ,   'x'
            ]
          for (message_i in flash[type]) {
            html.push('' + flash[type][message_i] + '');
          }
          html.push('
');
          $flash_holder.append(html.join(''));
        }
      }
      //e.stopPropagation();
    });

    $('body').on('ajax:html', '.modal > form', function(e, html, xhr) {
      $(this).find('.modal-body').html(html);
    });

    //$('body').on('ajax:json', function(e, data, xhr) {
    //});
  });
}(window.jQuery);
<|endoftext|>


<commit_before>Highcharts.setOptions({
  lang: {
    numericSymbols: null,  //otherwise by default ['k', 'M', 'G', 'T', 'P', 'E']
    thousandsSep: ','
  },
  chart: {
    style: {
      fontFamily: 'Quicksand'
    }
  }
});

function commonChartOptions(){
  return {
    title: { text: null },
    xAxis: { showEmpty: false },
    yAxis: { showEmpty: false },
    tooltip: {
      backgroundColor: null,
      borderWidth: 0,
      shadow: false,
      useHTML: true,
      style: {
          padding: 0
      }
    },
    legend: {
      align: 'center',
      margin: 20,
      verticalAlign: 'bottom',
      floating: false,
      backgroundColor: 'white',
      shadow: false
    },
    plotOptions: {
      series: {
        states: {
          inactive: {
            opacity: 1
          }
        }
      },
      bar: {
        stacking: 'normal',
      },
      column: {
        dataLabels: {
          color: '#232b49'
        },
      },
      pie: {
        allowPointSelect: true,
        colors: ["#9c3367", "#67347f", "#501e74", "#935fb8", "#e676a3", "#e4558b", "#7a9fb1", "#5297c6", "#97c086", "#3f7d69", "#6dc691", "#8e8d6b", "#e5c07c", "#e9d889", "#e59757", "#f4966c", "#e5644e", "#cd4851", "#bd4d65", "#515749"],
        cursor: 'pointer',
        dataLabels: { enabled: false },
        showInLegend: true,
        point: {
          events: {
            legendItemClick: function () {
              return false;
            }
          }
        }
      },
      line: {
        tooltip: {
          headerFormat: '{point.key}
',
          pointFormat: orderedPointFormat('kW')
        }
      },
      scatter: {
        marker: {
          radius: 5,
          states: {
            hover: {
              enabled: true,
              lineColor: 'rgb(100,100,100)'
            }
          }
        },
        states: {
          hover: {
            marker: {
              enabled: false
            }
          }
        },
        tooltip: {
          headerFormat: '{series.name}
',
          pointFormat: '{point.x:.2f} °C, {point.y:.2f} kWh'
        }
      }
    }
  };
}

function teachersChartOptions(pointFormat) {
  return {
    legend: {
      itemStyle: {
        fontWeight: 'normal'
      }
    },
    plotOptions: {
      series: {
        pointWidth: 30, groupPadding: 0.050
      },
      column: {
        tooltip: {
          headerFormat: '{series.name}
', pointFormat: pointFormat
        }
      }
    },
    xAxis: {
      lineWidth: 3, lineColor: 'rgb(151,151,151)'
    },
    yAxis: [{
      title: {
        text: "£"
      }
    }],
    navigation: {
      buttonOptions: {
        enabled: false
      }
    }
  }
}

function teachersColumn(chart_data, chart, seriesData) {

  console.log('Teachers column chart');

  var xAxisCategories = chart_data.x_axis_categories;
  var yAxisLabel = chart_data.y_axis_label;

  chart.xAxis[0].setCategories(xAxisCategories);
  chart.update(teachersChartOptions(orderedPointFormat(yAxisLabel)));

  Object.keys(seriesData).forEach(function (key) {
    // The false parameter stops it being redrawed after every addition of series data
    chart.addSeries(seriesData[key], false);
  });

  chart.redraw();
}

function barColumnLine(d, c, seriesData, chartType, noZoom) {
  var subChartType = d.chart1_subtype;
  console.log('bar or column or line ' + subChartType);

  var xAxisCategories = d.x_axis_categories;
  var yAxisLabel = d.y_axis_label;
  var y2AxisLabel = d.y2_axis_label;

  c.xAxis[0].setCategories(xAxisCategories);

  // BAR Charts
  if (chartType == 'bar') {
    console.log('bar');
    c.update({ chart: { inverted: true }, yAxis: [{ stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' } } }], plotOptions: { bar: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel)}}}});
  }

  // LINE charts
  if (chartType == 'line') {
    if (y2AxisLabel) {

      var axisTitle;
      var pointFormat;

      if (y2AxisLabel == 'Temperature') {
        axisTitle = '°C';
        pointFormat = '{point.y:.2f} °C';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Carbon')) {
        axisTitle = 'kWh';
        pointFormat = '{point.y:.2f} kWh';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Solar')) {
        axisTitle = 'W/m2';
        pointFormat = '{point.y:.2f} W/m2';
      }
      c.addAxis({ title: { text: axisTitle }, stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' }}, opposite: true});
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: pointFormat }}}});
    } else {
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: orderedPointFormat(yAxisLabel) }}}});
    }
  }

  // Column charts
  if (chartType == 'column') {
    console.log('column: ' + subChartType);
    if (! noZoom) {
      c.update({ chart: { zoomType: 'x'}, subtitle: { text: document.ontouchstart === undefined ?  'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in' }});
    }

    if (subChartType == 'stacked') {
      c.update({ plotOptions: { column: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel) }, stacking: 'normal'}}, yAxis: [{title: { text: yAxisLabel }, stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' } } }]});
    } else {
      c.update({ plotOptions: { column: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel)}}}});
    }

    if (y2AxisLabel) {

      var axisTitle;
      var pointFormat;
      var max;

      console.log('Y2 axis label' + y2AxisLabel);
      colour = '#232b49';
      if (y2AxisLabel == 'Temperature') {
        axisTitle = '°C';
        pointFormat = '{point.y:.2f} °C';
      } else if (y2AxisLabel == 'Degree Days') {
        axisTitle = 'Degree days';
        pointFormat = '{point.y:.2f} Degree days';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Carbon Intensity')) {
        axisTitle = 'kg/kWh';
        pointFormat = '{point.y:.2f} kg/kWh';
        max = 0.5;
      } else if (y2AxisLabel == 'Solar Irradiance') {
        axisTitle = 'W/m2';
        pointFormat = '{point.y:.2f} W/m2';
      }
      c.addAxis({ title: { text: axisTitle }, stackLabels: { style: { fontWeight: 'bold',  color: colour }}, opposite: true, max: max});
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: pointFormat }}}});
    }
  }

  Object.keys(seriesData).forEach(function (key) {
    console.log('Series data name: ' + seriesData[key].name);

    if (seriesData[key].name == 'CUSUM') {
      c.update({ plotOptions: { line: { tooltip: { pointFormat: '{point.y:.2f}', valueSuffix: yAxisLabel }}}});
    }

    if (isAStringAndStartsWith(seriesData[key].name, 'Energy') && seriesData[key].type == 'line') {
      console.log(seriesData[key]);
      seriesData[key].tooltip = { pointFormat: orderedPointFormat(yAxisLabel) }
    }
    // The false parameter stops it being redrawed after every addition of series data
    c.addSeries(seriesData[key], false);
  });

  updateChartLabels(d, c);
  normaliseYAxis(c);

  c.redraw();
}

function updateChartLabels(data, chart){

  var yAxisLabel = data.y_axis_label;
  var xAxisLabel = data.x_axis_label;

  if (yAxisLabel) {
    console.log('we have a yAxisLabel ' + yAxisLabel);
    chart.update({ yAxis: [{ title: { text: yAxisLabel }}]});
  }

  if (xAxisLabel) {
    console.log('we have a xAxisLabel ' + xAxisLabel);
    chart.update({ xAxis: [{ title: { text: xAxisLabel }}]});
  }
}

function isAStringAndStartsWith(thing, startingWith) {
  // IE Polyfill for startsWith
  if (!String.prototype.startsWith) {
    Object.defineProperty(String.prototype, 'startsWith', {
      value: function(search, pos) {
        pos = !pos || pos < 0 ? 0 : +pos;
        return this.substring(pos, pos + search.length) === search;
      }
    });
  }

  return (typeof thing === 'string' || thing instanceof String) && thing.startsWith(startingWith);
}

function scatter(d, c, seriesData) {
  console.log('scatter');

  updateChartLabels(d, c);
  c.update({chart: { type: 'scatter', zoomType: 'xy'}, subtitle: { text: document.ontouchstart === undefined ?  'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in' }});

  Object.keys(seriesData).forEach(function (key) {
    console.log(seriesData[key].name);
    c.addSeries(seriesData[key], false);
  });
  normaliseYAxis(c);
  c.redraw();
}

function pie(d, c, seriesData, $chartDiv) {
  $chartDiv.addClass('pie-chart');
  var chartHeight = $chartDiv.height();
  var yAxisLabel = d.y_axis_label;

  c.addSeries(seriesData, false);
  c.update({chart: {
    height: chartHeight,
    plotBackgroundColor: null,
    plotBorderWidth: null,
    plotShadow: false,
    type: 'pie'
  },
  plotOptions: {
   pie: {
    tooltip: {
        headerFormat: '{point.key}
',
        pointFormat: orderedPointFormat(yAxisLabel)
      }
    }
  }
  });
  c.redraw();
}

function orderedPointFormat(label){
  var format = '{point.y:.2f}';
  if(label == '£'){
    return label + format;
  } else {
    return format + ' ' + label;
  }
}

// If there are 2 axis and one or both of them dips under 0
// then make sure the scales are in the same ratio

function normaliseYAxis(chart){
  if(chart.yAxis.length === 2){
    var firstAxis = chart.yAxis[0];
    var firstExtremes = firstAxis.getExtremes();
    var secondAxis = chart.yAxis[1];
    var secondExtremes = secondAxis.getExtremes();
    if((firstExtremes.min < 0 && secondExtremes.min >= 0)){
      normaliseAxis(secondAxis, firstExtremes, secondExtremes);
    }
    if((firstExtremes.min >= 0 && secondExtremes.min <  0)){
      normaliseAxis(firstAxis, secondExtremes, firstExtremes);
    }
    if((firstExtremes.min < 0 && secondExtremes.min <  0)){
      var firstMinMaxRatio = firstExtremes.max / firstExtremes.min;
      var secondMinMaxRatio = secondExtremes.max / secondExtremes.min;
      if(firstMinMaxRatio > secondMinMaxRatio){
        normaliseAxis(secondAxis, firstExtremes, secondExtremes);
      } else {
        normaliseAxis(firstAxis, secondExtremes, firstExtremes);
      }
    }
  }
}

function normaliseAxis(axisToChange, axisAExtremes, axisBExtremes){
  var ratio = axisAExtremes.max / axisAExtremes.min;
  axisToChange.setExtremes((axisBExtremes.max / ratio), axisBExtremes.max)
}
<commit_msg>Tweak teacher config<commit_after>Highcharts.setOptions({
  lang: {
    numericSymbols: null,  //otherwise by default ['k', 'M', 'G', 'T', 'P', 'E']
    thousandsSep: ','
  },
  chart: {
    style: {
      fontFamily: 'Quicksand'
    }
  }
});

function commonChartOptions(){
  return {
    title: { text: null },
    xAxis: { showEmpty: false },
    yAxis: { showEmpty: false },
    tooltip: {
      backgroundColor: null,
      borderWidth: 0,
      shadow: false,
      useHTML: true,
      style: {
          padding: 0
      }
    },
    legend: {
      align: 'center',
      margin: 20,
      verticalAlign: 'bottom',
      floating: false,
      backgroundColor: 'white',
      shadow: false
    },
    plotOptions: {
      series: {
        states: {
          inactive: {
            opacity: 1
          }
        }
      },
      bar: {
        stacking: 'normal',
      },
      column: {
        dataLabels: {
          color: '#232b49'
        },
      },
      pie: {
        allowPointSelect: true,
        colors: ["#9c3367", "#67347f", "#501e74", "#935fb8", "#e676a3", "#e4558b", "#7a9fb1", "#5297c6", "#97c086", "#3f7d69", "#6dc691", "#8e8d6b", "#e5c07c", "#e9d889", "#e59757", "#f4966c", "#e5644e", "#cd4851", "#bd4d65", "#515749"],
        cursor: 'pointer',
        dataLabels: { enabled: false },
        showInLegend: true,
        point: {
          events: {
            legendItemClick: function () {
              return false;
            }
          }
        }
      },
      line: {
        tooltip: {
          headerFormat: '{point.key}
',
          pointFormat: orderedPointFormat('kW')
        }
      },
      scatter: {
        marker: {
          radius: 5,
          states: {
            hover: {
              enabled: true,
              lineColor: 'rgb(100,100,100)'
            }
          }
        },
        states: {
          hover: {
            marker: {
              enabled: false
            }
          }
        },
        tooltip: {
          headerFormat: '{series.name}
',
          pointFormat: '{point.x:.2f} °C, {point.y:.2f} kWh'
        }
      }
    }
  };
}

function teachersChartOptions(pointFormat) {
  return {
    legend: {
      itemStyle: {
        fontWeight: 'normal'
      }
    },
    plotOptions: {
      series: {
        pointPadding: 0.02, groupPadding: 0.005
      },
      column: {
        tooltip: {
          headerFormat: '{series.name}
', pointFormat: pointFormat
        }
      }
    },
    xAxis: {
      lineWidth: 3, lineColor: 'rgb(151,151,151)'
    },
    yAxis: [{
      title: {
        text: "£"
      }
    }],
    navigation: {
      buttonOptions: {
        enabled: false
      }
    }
  }
}

function teachersColumn(chart_data, chart, seriesData) {

  console.log('Teachers column chart');

  var xAxisCategories = chart_data.x_axis_categories;
  var yAxisLabel = chart_data.y_axis_label;

  chart.xAxis[0].setCategories(xAxisCategories);
  chart.update(teachersChartOptions(orderedPointFormat(yAxisLabel)));

  Object.keys(seriesData).forEach(function (key) {
    // The false parameter stops it being redrawed after every addition of series data
    chart.addSeries(seriesData[key], false);
  });

  chart.redraw();
}

function barColumnLine(d, c, seriesData, chartType, noZoom) {
  var subChartType = d.chart1_subtype;
  console.log('bar or column or line ' + subChartType);

  var xAxisCategories = d.x_axis_categories;
  var yAxisLabel = d.y_axis_label;
  var y2AxisLabel = d.y2_axis_label;

  c.xAxis[0].setCategories(xAxisCategories);

  // BAR Charts
  if (chartType == 'bar') {
    console.log('bar');
    c.update({ chart: { inverted: true }, yAxis: [{ stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' } } }], plotOptions: { bar: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel)}}}});
  }

  // LINE charts
  if (chartType == 'line') {
    if (y2AxisLabel) {

      var axisTitle;
      var pointFormat;

      if (y2AxisLabel == 'Temperature') {
        axisTitle = '°C';
        pointFormat = '{point.y:.2f} °C';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Carbon')) {
        axisTitle = 'kWh';
        pointFormat = '{point.y:.2f} kWh';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Solar')) {
        axisTitle = 'W/m2';
        pointFormat = '{point.y:.2f} W/m2';
      }
      c.addAxis({ title: { text: axisTitle }, stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' }}, opposite: true});
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: pointFormat }}}});
    } else {
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: orderedPointFormat(yAxisLabel) }}}});
    }
  }

  // Column charts
  if (chartType == 'column') {
    console.log('column: ' + subChartType);
    if (! noZoom) {
      c.update({ chart: { zoomType: 'x'}, subtitle: { text: document.ontouchstart === undefined ?  'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in' }});
    }

    if (subChartType == 'stacked') {
      c.update({ plotOptions: { column: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel) }, stacking: 'normal'}}, yAxis: [{title: { text: yAxisLabel }, stackLabels: { style: { fontWeight: 'bold',  color: '#232b49' } } }]});
    } else {
      c.update({ plotOptions: { column: { tooltip: { headerFormat: '{series.name}
', pointFormat: orderedPointFormat(yAxisLabel)}}}});
    }

    if (y2AxisLabel) {

      var axisTitle;
      var pointFormat;
      var max;

      console.log('Y2 axis label' + y2AxisLabel);
      colour = '#232b49';
      if (y2AxisLabel == 'Temperature') {
        axisTitle = '°C';
        pointFormat = '{point.y:.2f} °C';
      } else if (y2AxisLabel == 'Degree Days') {
        axisTitle = 'Degree days';
        pointFormat = '{point.y:.2f} Degree days';
      } else if (isAStringAndStartsWith(y2AxisLabel, 'Carbon Intensity')) {
        axisTitle = 'kg/kWh';
        pointFormat = '{point.y:.2f} kg/kWh';
        max = 0.5;
      } else if (y2AxisLabel == 'Solar Irradiance') {
        axisTitle = 'W/m2';
        pointFormat = '{point.y:.2f} W/m2';
      }
      c.addAxis({ title: { text: axisTitle }, stackLabels: { style: { fontWeight: 'bold',  color: colour }}, opposite: true, max: max});
      c.update({ plotOptions: { line: { tooltip: { headerFormat: '{point.key}
',  pointFormat: pointFormat }}}});
    }
  }

  Object.keys(seriesData).forEach(function (key) {
    console.log('Series data name: ' + seriesData[key].name);

    if (seriesData[key].name == 'CUSUM') {
      c.update({ plotOptions: { line: { tooltip: { pointFormat: '{point.y:.2f}', valueSuffix: yAxisLabel }}}});
    }

    if (isAStringAndStartsWith(seriesData[key].name, 'Energy') && seriesData[key].type == 'line') {
      console.log(seriesData[key]);
      seriesData[key].tooltip = { pointFormat: orderedPointFormat(yAxisLabel) }
    }
    // The false parameter stops it being redrawed after every addition of series data
    c.addSeries(seriesData[key], false);
  });

  updateChartLabels(d, c);
  normaliseYAxis(c);

  c.redraw();
}

function updateChartLabels(data, chart){

  var yAxisLabel = data.y_axis_label;
  var xAxisLabel = data.x_axis_label;

  if (yAxisLabel) {
    console.log('we have a yAxisLabel ' + yAxisLabel);
    chart.update({ yAxis: [{ title: { text: yAxisLabel }}]});
  }

  if (xAxisLabel) {
    console.log('we have a xAxisLabel ' + xAxisLabel);
    chart.update({ xAxis: [{ title: { text: xAxisLabel }}]});
  }
}

function isAStringAndStartsWith(thing, startingWith) {
  // IE Polyfill for startsWith
  if (!String.prototype.startsWith) {
    Object.defineProperty(String.prototype, 'startsWith', {
      value: function(search, pos) {
        pos = !pos || pos < 0 ? 0 : +pos;
        return this.substring(pos, pos + search.length) === search;
      }
    });
  }

  return (typeof thing === 'string' || thing instanceof String) && thing.startsWith(startingWith);
}

function scatter(d, c, seriesData) {
  console.log('scatter');

  updateChartLabels(d, c);
  c.update({chart: { type: 'scatter', zoomType: 'xy'}, subtitle: { text: document.ontouchstart === undefined ?  'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in' }});

  Object.keys(seriesData).forEach(function (key) {
    console.log(seriesData[key].name);
    c.addSeries(seriesData[key], false);
  });
  normaliseYAxis(c);
  c.redraw();
}

function pie(d, c, seriesData, $chartDiv) {
  $chartDiv.addClass('pie-chart');
  var chartHeight = $chartDiv.height();
  var yAxisLabel = d.y_axis_label;

  c.addSeries(seriesData, false);
  c.update({chart: {
    height: chartHeight,
    plotBackgroundColor: null,
    plotBorderWidth: null,
    plotShadow: false,
    type: 'pie'
  },
  plotOptions: {
   pie: {
    tooltip: {
        headerFormat: '{point.key}
',
        pointFormat: orderedPointFormat(yAxisLabel)
      }
    }
  }
  });
  c.redraw();
}

function orderedPointFormat(label){
  var format = '{point.y:.2f}';
  if(label == '£'){
    return label + format;
  } else {
    return format + ' ' + label;
  }
}

// If there are 2 axis and one or both of them dips under 0
// then make sure the scales are in the same ratio

function normaliseYAxis(chart){
  if(chart.yAxis.length === 2){
    var firstAxis = chart.yAxis[0];
    var firstExtremes = firstAxis.getExtremes();
    var secondAxis = chart.yAxis[1];
    var secondExtremes = secondAxis.getExtremes();
    if((firstExtremes.min < 0 && secondExtremes.min >= 0)){
      normaliseAxis(secondAxis, firstExtremes, secondExtremes);
    }
    if((firstExtremes.min >= 0 && secondExtremes.min <  0)){
      normaliseAxis(firstAxis, secondExtremes, firstExtremes);
    }
    if((firstExtremes.min < 0 && secondExtremes.min <  0)){
      var firstMinMaxRatio = firstExtremes.max / firstExtremes.min;
      var secondMinMaxRatio = secondExtremes.max / secondExtremes.min;
      if(firstMinMaxRatio > secondMinMaxRatio){
        normaliseAxis(secondAxis, firstExtremes, secondExtremes);
      } else {
        normaliseAxis(firstAxis, secondExtremes, firstExtremes);
      }
    }
  }
}

function normaliseAxis(axisToChange, axisAExtremes, axisBExtremes){
  var ratio = axisAExtremes.max / axisAExtremes.min;
  axisToChange.setExtremes((axisBExtremes.max / ratio), axisBExtremes.max)
}
<|endoftext|>