<commit_before>// global module:false

// var path = require('path');

var jsList = [
    '<%= sourceDir %>/js/vendor/jquery-1.11.3.min.js',
    '<%= sourceDir %>/js/ui.js',
    '<%= sourceDir %>/js/app.js'
];

var uncssIgnoreClass = [
    '/^.active/',
    '/^.open/',
    '/^.is/',
    '/^.has/',
    '/^.slick/'
];

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

    grunt.initConfig({
        baseDir: 'static',
        sourceDir: '<%= baseDir %>/src',
        publicDir: '<%= baseDir %>/pub',

        watch: {
            options: {
                livereload: true
            },

            less: {
                files: ['<%= sourceDir %>/less/**/*.less'],
                tasks: ['less:dev', 'autoprefixer:sourcemap']
            },

            includes: {
                files: ['<%= baseDir %>/layouts/_includes/**/*'],
                tasks: ['preprocess:html']
            },

            jshint: {
                files: ['<%= sourceDir %>/js/ui.js'],
                tasks: ['jshint:all']
            },

            jscs: {
                files: ['<%= sourceDir %>/js/*.js'],
                tasks: ['jscs:src']
            },

            sprite: {
                files: ['<%= sourceDir %>/img/ui-icons/**/*.png'],
                tasks: ['sprite', 'less:dev', 'autoprefixer:sourcemap']
            }
        },

        jshint: {
            all: ['<%= sourceDir %>/js/ui.js'],

            options: {
                jshintrc: '.jshintrc',
            }
        },

        jscs: {
            src: '<%= sourceDir %>/js/*.js',

            options: {
                config: '.jscsrc'
            }
        },

        sprite: {
            compile: {
                src: '<%= sourceDir %>/img/ui-icons/**/*.png',
                retinaSrcFilter: ['<%= sourceDir %>/img/ui-icons/**/*@2x.png'],
                dest: '<%= sourceDir %>/img/sprites/ui-icons.png',
                retinaDest: '<%= sourceDir %>/img/sprites/ui-icons@2x.png',
                destCss: '<%= sourceDir %>/less/~ui-icons.less',
                cssFormat: 'css_retina',
                padding: 1,
                cssTemplate: function (data) {
                    var classMediaRespond = '.icon';
                    var classNamespace = '.i-';

                    var result = classMediaRespond +
                        '{background-image:url(' + data.spritesheet.image + ');}' +
                        '@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {' +
                        classMediaRespond + '{' +
                        'background-image:url(' + data.retina_spritesheet.image + ');' +
                        'background-size:' + data.spritesheet.px.width + ' ' + data.spritesheet.px.height +
                        ';}}';

                    for (var i = 0; i < data.items.length; i++) {
                        result += '@' + data.items[i].name + '-bg-position:' + data.items[i].offset_x + 'px ' + data.items[i].offset_y + 'px;' +
                            '@' + data.items[i].name + '-width:' + data.items[i].width + 'px;' +
                            '@' + data.items[i].name + '-height:' + data.items[i].height + 'px;' +
                            classNamespace + data.items[i].name + '{' +
                            'background-position:' + data.items[i].offset_x + 'px ' + data.items[i].offset_y + 'px;' +
                            'width:' + data.items[i].width + 'px;' +
                            'height:' + data.items[i].height + 'px;' +
                            '}';
                    }

                    return result;
                }
            }
        },

        less: {
            dev: {
                options: {
                    sourceMap: true,
                    sourceMapFilename: 'static/src/css/app.css.map',  // Path to save map
                    sourceMapBasepath: 'static',                      // Deleting excessive prefixes
                    sourceMapRootpath: '/'                            // Path prefix (*.map file)
                },

                files: {
                    '<%= sourceDir %>/css/app.css': ['<%= sourceDir %>/less/app.less']
                }
            },

            production: {
                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= sourceDir %>/less/app.less']
                }
            }
        },

        cssmin: {
            options: {
                roundingPrecision: -1
            },

            target: {
                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= publicDir %>/css/app.min.css']
                }
            }
        },

        uncss: {
            dist: {
                options: {
                    ignore: uncssIgnoreClass,
                    csspath: '../../',
                    stylesheets: ['<%= publicDir %>/css/app.min.css']
                },

                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= baseDir %>/layouts/*.html']
                }
            }
        },

        autoprefixer: {
            options: {
                browsers: ['last 3 versions', 'ie 9']
            },

            sourcemap: {
                options: {
                    map: {
                        prev: '<%= sourceDir %>/css/'
                    }
                },

                src: '<%= sourceDir %>/css/app.css',
                dest: '<%= sourceDir %>/css/app.css'
            },

            pub: {
                src: '<%= publicDir %>/css/app.min.css',
                dest: '<%= publicDir %>/css/app.min.css'
            },
        },

        preprocess: {
            options: {
                context : {
                    DEBUG: true
                }
            },

            html: {
                files: [{
                    expand: true,
                    cwd: '<%= baseDir %>/layouts/_includes',
                    src: 'tpl.*',
                    dest: '<%= baseDir %>/layouts/',
                    rename: function (dest, src) {
                        return dest + src.replace('tpl.', '');
                    }
                }]
            }
        },

        clean: {
            css: ['<%= publicDir %>/css'],
            js: ['<%= publicDir %>/js'],
            img: ['<%= publicDir %>/img'],
            srcjs: ['<%= sourceDir %>/js/*.js.gz']
        },

        copy: {
            img: {
                cwd: '<%= sourceDir %>/img/',
                src: ['**/*',
                    '!ui-icons',
                    '!ui-icons/**/*',
                    '!zzz',
                    '!zzz/**/*'//,
                    // '!atom',
                    // '!atom/**/*'
                ],
                dest: '<%= publicDir %>/img/',
                expand: true // required when using cwd
            },

            font: {
                cwd: '<%= sourceDir %>',
                src: ['font/**/*'],
                dest: '<%= publicDir %>/',
                expand: true
            }

        },

        uglify: {
            js: {
                files: [
                    {
                        src: jsList,
                        dest: '<%= publicDir %>/js/app.min.js'
                    }
                ]
            }
        },

        filerev: {
            //  options: {
            //    algorithm: 'md5', // def: md5
            //    length: 8         // def: 8
            //  },

            js: {
                src: ['<%= publicDir %>/js/app.min.js'],
                dest: '<%= publicDir %>/js/'
            },

            css: {
                src: ['<%= publicDir %>/css/app.min.css'],
                dest: '<%= publicDir %>/css/'
            },

            img: {
                src: '<%= publicDir %>/img/**/*.{jpg,jpeg,png,gif}'
            }
        },

        filerev_assets: {
            dist: {
                options: {
                    prettyPrint: true,
                    cwd: '<%= publicDir %>/',
                    dest: '<%= publicDir %>/manifest.json'
                }
            }
        },

        usemin: {
            css: '<%= publicDir %>/**/*.css',

            options: {
                assetsDirs: ['<%= publicDir %>'],

                patterns: {
                    css: [
                        [/(img\/.*?\.(?:png|jpe?g|gif))/gm, 'Replacing reference to *.png, *.jpg, *.jpeg, *.gif']
                    ]
                }
            }
        },

        compress: {
            js: {
                options: {
                    mode: 'gzip',
                    pretty: true,
                    level: 9
                },

                files: [
                    // Each of the files in the pub/ folder will be output to
                    // the pub/ folder with the extension .gz
                    {
                        expand: true,
                        src: ['<%= publicDir %>/js/*.js'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    },

                    {
                        expand: true,
                        src: ['<%= sourceDir %>/js/vendor/modernizr-*.js'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    }
                ]
            },

            css: {
                options: {
                    mode: 'gzip',
                    pretty: true,
                    level: 9
                },

                files: [
                    // Each of the files in the pub/ folder will be output to
                    // the pub/ folder with the extension .gz
                    {
                        expand: true,
                        src: ['<%= publicDir %>/css/*.css'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    }
                ]
            }
        },

        modernizr: {
            dist: {
                'crawl': false,
                'dest': '<%= sourceDir %>/js/vendor/modernizr.min.js',
                'tests': [
                    // 'touchevents'
                ],
                'options': [
                    'domPrefixes',
                    'prefixes',
                    'testAllProps',
                    'testProp',
                    'testStyles',
                    'setClasses'
                ],
                'uglify': true
            }
        },

        chmod: {
            staticDirs: {
                options: {
                    mode: '755'
                },
                src: ['**/*', '!node_modules/**/*'],
                filter: 'isDirectory'
            },

            staticFiles: {
                options: {
                    mode: '644'
                },
                src: ['**/*', '!node_modules/**/*'],
                filter: 'isFile'
            }
        }
    });

    // Load tasks
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-preprocess');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-filerev');
    grunt.loadNpmTasks('grunt-filerev-assets');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-compress');
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-jscs');
    grunt.loadNpmTasks('grunt-modernizr');
    grunt.loadNpmTasks('grunt-chmod');
    grunt.loadNpmTasks('grunt-spritesmith');
    grunt.loadNpmTasks('grunt-uncss');
    grunt.loadNpmTasks('grunt-contrib-cssmin');

    // Register tasks
    grunt.registerTask('spritesheet', [
        'sprite',
        'less',
        'autoprefixer:sourcemap'
    ]);

    grunt.registerTask('build', [
        'clean',
        'uglify', // minify js
        'less:production', // minify css
        'uncss',
        'cssmin',
        'autoprefixer:pub',
        'copy:img',
        'filerev:img',
        'filerev:js', 'filerev:css', 'filerev_assets',
        'usemin:css',
        'copy:font'
    ]);

    grunt.registerTask('gzip-css', [
        'compress:css'
    ]);

    grunt.registerTask('gzip-js', [
        'compress:js'
    ]);
};
<commit_msg>uncss reg<commit_after>// global module:false

// var path = require('path');

var jsList = [
    '<%= sourceDir %>/js/vendor/jquery-1.11.3.min.js',
    '<%= sourceDir %>/js/ui.js',
    '<%= sourceDir %>/js/app.js'
];

var uncssIgnoreClass = [
    /^.o-/,
    /^.u-/,
    /^.c-/,
    /^.t-/,
    /^.s-/,
    /^.is-/,
    /^.has-/,
    /^.js-/,
    /^.i-/,
    /^.active/, /*bootstrap*/
    /^.open/,
    /^.modal/,
    /^.in/,
    /^.fade/,
    /^.slick/, /*slick*/
];

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

    grunt.initConfig({
        baseDir: 'static',
        sourceDir: '<%= baseDir %>/src',
        publicDir: '<%= baseDir %>/pub',

        watch: {
            options: {
                livereload: true
            },

            less: {
                files: ['<%= sourceDir %>/less/**/*.less'],
                tasks: ['less:dev', 'autoprefixer:sourcemap']
            },

            includes: {
                files: ['<%= baseDir %>/layouts/_includes/**/*'],
                tasks: ['preprocess:html']
            },

            jshint: {
                files: ['<%= sourceDir %>/js/ui.js'],
                tasks: ['jshint:all']
            },

            jscs: {
                files: ['<%= sourceDir %>/js/*.js'],
                tasks: ['jscs:src']
            },

            sprite: {
                files: ['<%= sourceDir %>/img/ui-icons/**/*.png'],
                tasks: ['sprite', 'less:dev', 'autoprefixer:sourcemap']
            }
        },

        jshint: {
            all: ['<%= sourceDir %>/js/ui.js'],

            options: {
                jshintrc: '.jshintrc',
            }
        },

        jscs: {
            src: '<%= sourceDir %>/js/*.js',

            options: {
                config: '.jscsrc'
            }
        },

        sprite: {
            compile: {
                src: '<%= sourceDir %>/img/ui-icons/**/*.png',
                retinaSrcFilter: ['<%= sourceDir %>/img/ui-icons/**/*@2x.png'],
                dest: '<%= sourceDir %>/img/sprites/ui-icons.png',
                retinaDest: '<%= sourceDir %>/img/sprites/ui-icons@2x.png',
                destCss: '<%= sourceDir %>/less/~ui-icons.less',
                cssFormat: 'css_retina',
                padding: 1,
                cssTemplate: function (data) {
                    var classMediaRespond = '.icon';
                    var classNamespace = '.i-';

                    var result = classMediaRespond +
                        '{background-image:url(' + data.spritesheet.image + ');}' +
                        '@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {' +
                        classMediaRespond + '{' +
                        'background-image:url(' + data.retina_spritesheet.image + ');' +
                        'background-size:' + data.spritesheet.px.width + ' ' + data.spritesheet.px.height +
                        ';}}';

                    for (var i = 0; i < data.items.length; i++) {
                        result += '@' + data.items[i].name + '-bg-position:' + data.items[i].offset_x + 'px ' + data.items[i].offset_y + 'px;' +
                            '@' + data.items[i].name + '-width:' + data.items[i].width + 'px;' +
                            '@' + data.items[i].name + '-height:' + data.items[i].height + 'px;' +
                            classNamespace + data.items[i].name + '{' +
                            'background-position:' + data.items[i].offset_x + 'px ' + data.items[i].offset_y + 'px;' +
                            'width:' + data.items[i].width + 'px;' +
                            'height:' + data.items[i].height + 'px;' +
                            '}';
                    }

                    return result;
                }
            }
        },

        less: {
            dev: {
                options: {
                    sourceMap: true,
                    sourceMapFilename: 'static/src/css/app.css.map',  // Path to save map
                    sourceMapBasepath: 'static',                      // Deleting excessive prefixes
                    sourceMapRootpath: '/'                            // Path prefix (*.map file)
                },

                files: {
                    '<%= sourceDir %>/css/app.css': ['<%= sourceDir %>/less/app.less']
                }
            },

            production: {
                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= sourceDir %>/less/app.less']
                }
            }
        },

        cssmin: {
            options: {
                roundingPrecision: -1
            },

            target: {
                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= publicDir %>/css/app.min.css']
                }
            }
        },

        uncss: {
            dist: {
                options: {
                    ignore: uncssIgnoreClass,
                    csspath: '../../',
                    stylesheets: ['<%= publicDir %>/css/app.min.css']
                },

                files: {
                    '<%= publicDir %>/css/app.min.css': ['<%= baseDir %>/layouts/*.html']
                }
            }
        },

        autoprefixer: {
            options: {
                browsers: ['last 3 versions', 'ie 9']
            },

            sourcemap: {
                options: {
                    map: {
                        prev: '<%= sourceDir %>/css/'
                    }
                },

                src: '<%= sourceDir %>/css/app.css',
                dest: '<%= sourceDir %>/css/app.css'
            },

            pub: {
                src: '<%= publicDir %>/css/app.min.css',
                dest: '<%= publicDir %>/css/app.min.css'
            },
        },

        preprocess: {
            options: {
                context : {
                    DEBUG: true
                }
            },

            html: {
                files: [{
                    expand: true,
                    cwd: '<%= baseDir %>/layouts/_includes',
                    src: 'tpl.*',
                    dest: '<%= baseDir %>/layouts/',
                    rename: function (dest, src) {
                        return dest + src.replace('tpl.', '');
                    }
                }]
            }
        },

        clean: {
            css: ['<%= publicDir %>/css'],
            js: ['<%= publicDir %>/js'],
            img: ['<%= publicDir %>/img'],
            srcjs: ['<%= sourceDir %>/js/*.js.gz']
        },

        copy: {
            img: {
                cwd: '<%= sourceDir %>/img/',
                src: ['**/*',
                    '!ui-icons',
                    '!ui-icons/**/*',
                    '!zzz',
                    '!zzz/**/*'//,
                    // '!atom',
                    // '!atom/**/*'
                ],
                dest: '<%= publicDir %>/img/',
                expand: true // required when using cwd
            },

            font: {
                cwd: '<%= sourceDir %>',
                src: ['font/**/*'],
                dest: '<%= publicDir %>/',
                expand: true
            }

        },

        uglify: {
            js: {
                files: [
                    {
                        src: jsList,
                        dest: '<%= publicDir %>/js/app.min.js'
                    }
                ]
            }
        },

        filerev: {
            //  options: {
            //    algorithm: 'md5', // def: md5
            //    length: 8         // def: 8
            //  },

            js: {
                src: ['<%= publicDir %>/js/app.min.js'],
                dest: '<%= publicDir %>/js/'
            },

            css: {
                src: ['<%= publicDir %>/css/app.min.css'],
                dest: '<%= publicDir %>/css/'
            },

            img: {
                src: '<%= publicDir %>/img/**/*.{jpg,jpeg,png,gif}'
            }
        },

        filerev_assets: {
            dist: {
                options: {
                    prettyPrint: true,
                    cwd: '<%= publicDir %>/',
                    dest: '<%= publicDir %>/manifest.json'
                }
            }
        },

        usemin: {
            css: '<%= publicDir %>/**/*.css',

            options: {
                assetsDirs: ['<%= publicDir %>'],

                patterns: {
                    css: [
                        [/(img\/.*?\.(?:png|jpe?g|gif))/gm, 'Replacing reference to *.png, *.jpg, *.jpeg, *.gif']
                    ]
                }
            }
        },

        compress: {
            js: {
                options: {
                    mode: 'gzip',
                    pretty: true,
                    level: 9
                },

                files: [
                    // Each of the files in the pub/ folder will be output to
                    // the pub/ folder with the extension .gz
                    {
                        expand: true,
                        src: ['<%= publicDir %>/js/*.js'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    },

                    {
                        expand: true,
                        src: ['<%= sourceDir %>/js/vendor/modernizr-*.js'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    }
                ]
            },

            css: {
                options: {
                    mode: 'gzip',
                    pretty: true,
                    level: 9
                },

                files: [
                    // Each of the files in the pub/ folder will be output to
                    // the pub/ folder with the extension .gz
                    {
                        expand: true,
                        src: ['<%= publicDir %>/css/*.css'],
                        rename: function (dest, src) {
                            return src + '.gz';
                        }
                    }
                ]
            }
        },

        modernizr: {
            dist: {
                'crawl': false,
                'dest': '<%= sourceDir %>/js/vendor/modernizr.min.js',
                'tests': [
                    // 'touchevents'
                ],
                'options': [
                    'domPrefixes',
                    'prefixes',
                    'testAllProps',
                    'testProp',
                    'testStyles',
                    'setClasses'
                ],
                'uglify': true
            }
        },

        chmod: {
            staticDirs: {
                options: {
                    mode: '755'
                },
                src: ['**/*', '!node_modules/**/*'],
                filter: 'isDirectory'
            },

            staticFiles: {
                options: {
                    mode: '644'
                },
                src: ['**/*', '!node_modules/**/*'],
                filter: 'isFile'
            }
        }
    });

    // Load tasks
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-preprocess');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-filerev');
    grunt.loadNpmTasks('grunt-filerev-assets');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-compress');
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-jscs');
    grunt.loadNpmTasks('grunt-modernizr');
    grunt.loadNpmTasks('grunt-chmod');
    grunt.loadNpmTasks('grunt-spritesmith');
    grunt.loadNpmTasks('grunt-uncss');
    grunt.loadNpmTasks('grunt-contrib-cssmin');

    // Register tasks
    grunt.registerTask('spritesheet', [
        'sprite',
        'less',
        'autoprefixer:sourcemap'
    ]);

    grunt.registerTask('build', [
        'clean',
        'uglify', // minify js
        'less:production', // minify css
        'uncss',
        'cssmin',
        'autoprefixer:pub',
        'copy:img',
        'filerev:img',
        'filerev:js', 'filerev:css', 'filerev_assets',
        'usemin:css',
        'copy:font'
    ]);

    grunt.registerTask('gzip-css', [
        'compress:css'
    ]);

    grunt.registerTask('gzip-js', [
        'compress:js'
    ]);
};
<|endoftext|>


<commit_before>'use strict'

const { test } = require('tap')
const mikealRequest = require('request')
const nock = require('..')

require('./cleanup_after_each')()

test('url encoding', t => {
  nock('http://example.test')
    .get('/key?a=[1]')
    .reply(200)

  mikealRequest('http://example.test/key?a=[1]', function(err, res) {
    if (err) throw err
    t.equal(res.statusCode, 200)
    t.end()
  })
})
<commit_msg>refactor: chai assertions for test_url_encoding (#1764)<commit_after>'use strict'

const { test } = require('tap')
const { expect } = require('chai')
const nock = require('..')
const got = require('./got_client')

require('./cleanup_after_each')()

test('url encoding', async t => {
  const scope = nock('http://example.test')
    .get('/key?a=[1]')
    .reply(200)

  const { statusCode } = await got('http://example.test/key?a=[1]')
  expect(statusCode).to.equal(200)

  scope.done()
})
<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * General dialog type
 * Used to live at 'JOIST/Dialog'. Moved to 'SUN/Dialog' on 4/10/2018
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  var Display = require( 'SCENERY/display/Display' );
  var FullScreen = require( 'SCENERY/util/FullScreen' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );
  var Path = require( 'SCENERY/nodes/Path' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var SunA11yStrings = require( 'SUN/SunA11yStrings' );
  var Tandem = require( 'TANDEM/Tandem' );
  var DialogIO = require( 'SUN/DialogIO' );

  // strings
  var closeString = SunA11yStrings.close.value;

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   * @constructor
   */
  function Dialog( content, options ) {

    var self = this;

    options = _.extend( {

      // Dialog-specific options
      modal: true, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center
      titleSpacing: 20, // {number} how far the title is placed above the content
      hasCloseButton: true, // whether to put a close 'X' button is upper-right corner

      // {function} which sets the dialog's position in global coordinates. called as
      // layoutStrategy( dialog, simBounds, screenBounds, scale )
      layoutStrategy: Dialog.DEFAULT_LAYOUT_STRATEGY,

      // close button options
      closeButtonBaseColor: '#d00',
      closeButtonMargin: 5, // {number} how far away should the close button be from the panel border
      closeButtonListener: function() { self.hide(); },

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      xMargin: 20,
      yMargin: 20,
      tandem: Tandem.optional,
      phetioType: DialogIO,
      phetioReadOnly: false, // default to false so it can pass it through to the close button
      phetioState: false, // default to false so it can pass it through to the close button

      // a11y options
      tagName: 'div',
      ariaRole: 'dialog',
      focusOnCloseNode: null // {Node} receives focus on close, if null focus returns to element that had focus on open
    }, options );

    // @private (read-only)
    this.isModal = options.modal;

    // @private
    this.showCallback = options.showCallback;
    this.hideCallback = options.hideCallback;

    // see https://github.com/phetsims/joist/issues/293
    assert && assert( this.isModal, 'Non-modal dialogs not currently supported' );

    // @private - whether the dialog is showing
    this.isShowing = false;

    var dialogContent = new Node( {
      children: [ content ]
    } );

    if ( options.title ) {

      var titleNode = options.title;
      dialogContent.addChild( titleNode );

      var updateTitlePosition = function() {
        switch( options.titleAlign ) {
          case 'center':
            titleNode.centerX = content.centerX;
            break;
          case 'left':
            titleNode.left = content.left;
            break;
          case 'right':
            titleNode.right = content.right;
            break;
          default:
            throw new Error( 'unknown titleAlign for Dialog: ' + options.titleAlign );
        }
        titleNode.bottom = content.top - options.titleSpacing;
      };

      if ( options.resize ) {
        content.on( 'bounds', updateTitlePosition );
        titleNode.on( 'localBounds', updateTitlePosition );
      }
      updateTitlePosition();
    }

    Panel.call( this, dialogContent, options );

    if ( options.hasCloseButton ) {

      var crossSize = 10;
      var crossNode = new Path( new Shape().moveTo( 0, 0 ).lineTo( crossSize, crossSize ).moveTo( 0, crossSize ).lineTo( crossSize, 0 ), {
        stroke: '#fff',
        lineWidth: 3
      } );

      var closeButton = new RectangularPushButton( {
        content: crossNode,
        baseColor: options.closeButtonBaseColor,
        xMargin: 5,
        yMargin: 5,
        listener: options.closeButtonListener,
        accessibleFire: function() {
          self.focusActiveElement();
        },
        tandem: options.tandem.createTandem( 'closeButton' ),
        phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog
        phetioState: options.phetioState, // match the state transfer of the Dialog

        // a11y options
        tagName: 'button',
        innerContent: closeString
      } );
      this.addChild( closeButton );

      var updateClosePosition = function() {
        closeButton.right = dialogContent.right + options.xMargin - options.closeButtonMargin;
        closeButton.top = dialogContent.top - options.yMargin + options.closeButtonMargin;
      };

      if ( options.resize ) {
        dialogContent.on( 'bounds', updateClosePosition );
        if ( options.title ) {
          options.title.on( 'bounds', updateClosePosition );
        }
      }
      updateClosePosition();
    }

    var sim = window.phet.joist.sim;

    // @private
    this.updateLayout = function() {
      options.layoutStrategy( self, sim.boundsProperty.value, sim.screenBoundsProperty.value, sim.scaleProperty.value );
    };

    this.updateLayout();

    // @private
    this.sim = sim;

    // a11y - set the order of content for accessibility, title before content
    this.accessibleOrder = [ titleNode, dialogContent ];

    // a11y - set the aria labelledby and describedby relations so that whenever focus enters the dialog, the title
    // and description content are read in full
    content.tagName && this.setAriaDescribedByNode( content );
    if ( options.title ) {
      options.title.tagName && this.setAriaLabelledByNode( options.title );
    }

    // must be removed on dispose
    this.sim.resizedEmitter.addListener( this.updateLayout );

    // @private (a11y) - the active element when the dialog is shown, tracked so that focus can be restored on close
    this.activeElement = options.focusOnCloseNode || null;

    // a11y - close the dialog when pressing "escape"
    var escapeListener = this.addAccessibleInputListener( {
      keydown: function( event ) {
        if ( event.keyCode === KeyboardUtil.KEY_ESCAPE ) {
          event.preventDefault();
          self.hide();
          self.focusActiveElement();
        }
        else if ( event.keyCode === KeyboardUtil.KEY_TAB && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          var activeId = Display.focus.trail.getUniqueId();
          var noNextFocusable = AccessibilityUtil.getNextFocusable().id === activeId;
          var noPreviousFocusable = AccessibilityUtil.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            event.preventDefault();
          }
        }
      }
    } );

    // @private - to be called on dispose()
    this.disposeDialog = function() {
      self.sim.resizedEmitter.removeListener( self.updateLayout );
      self.removeAccessibleInputListener( escapeListener );

      if ( options.hasCloseButton ) {
        closeButton.dispose();

        if ( options.resize ) {
          dialogContent.off( 'bounds', updateClosePosition );
          if ( options.title ) {
            options.title.off( 'bounds', updateClosePosition );
            titleNode.off( 'localBounds', updateTitlePosition );
            content.off( 'bounds', updateTitlePosition );
          }
        }
      }

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  sun.register( 'Dialog', Dialog );

  // @private
  Dialog.DEFAULT_LAYOUT_STRATEGY = function( dialog, simBounds, screenBounds, scale ) {

    // The size is set in the Sim.topLayer, but we need to update the location here
    dialog.center = simBounds.center.times( 1.0 / scale );
  };

  return inherit( Panel, Dialog, {

    // @public
    show: function() {
      if ( !this.isShowing ) {
        window.phet.joist.sim.showPopup( this, this.isModal );
        this.isShowing = true;

        // a11y - store the currently active element before hiding all other accessible content
        // so that the active element isn't blurred
        this.activeElement = this.activeElement || Display.focusedNode;
        this.setAccessibleViewsVisible( false );

        // In case the window size has changed since the dialog was hidden, we should try layout out again.
        // See https://github.com/phetsims/joist/issues/362
        this.updateLayout();

        // Do this last
        this.showCallback && this.showCallback();
      }
    },

    /**
     * Hide the dialog.  If you create a new dialog next time you show(), be sure to dispose this
     * dialog instead.
     * @public
     */
    hide: function() {
      if ( this.isShowing ) {

        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;

        // a11y - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.setAccessibleViewsVisible( true );

        // Do this last
        this.hideCallback && this.hideCallback();
      }
    },

    /**
     * Make eligible for garbage collection.
     * @public
     */
    dispose: function() {
      this.hide();
      this.disposeDialog();
      Panel.prototype.dispose.call( this );
    },

    /**
     * Hide or show all accessible content related to the sim ScreenViews, navigation bar, and alert content. Instead
     * of using setVisible, we have to remove the subtree of accessible content from each view element in order to
     * prevent an IE11 bug where content remains invisible in the accessibility tree, see
     * https://github.com/phetsims/john-travoltage/issues/247
     *
     * @param {boolean} visible
     */
    setAccessibleViewsVisible: function( visible ) {
      for ( var i = 0; i < this.sim.screens.length; i++ ) {
        this.sim.screens[ i ].view.accessibleContentDisplayed = visible;
      }
      this.sim.navigationBar.accessibleContentDisplayed = visible;
      this.sim.homeScreen && this.sim.homeScreen.view.setAccessibleContentDisplayed( visible );

      // workaround for a strange Edge bug where this child of the navigation bar remains visible,
      // see https://github.com/phetsims/a11y-research/issues/30
      if ( this.sim.navigationBar.keyboardHelpButton ) {
        this.sim.navigationBar.keyboardHelpButton.accessibleVisible = visible;
      }
    },

    /**
     * If there is an active element, focus it.  Should almost always be closed after the Dialog has been closed.
     *
     * @public
     * @a11y
     */
    focusActiveElement: function() {
      this.activeElement && this.activeElement.focus();
    }
  } );
} );<commit_msg>update usage of addAccessibleInputListener, see phetsims/scenery#764<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * General dialog type
 * Used to live at 'JOIST/Dialog'. Moved to 'SUN/Dialog' on 4/10/2018
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  var Display = require( 'SCENERY/display/Display' );
  var FullScreen = require( 'SCENERY/util/FullScreen' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );
  var Path = require( 'SCENERY/nodes/Path' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var SunA11yStrings = require( 'SUN/SunA11yStrings' );
  var Tandem = require( 'TANDEM/Tandem' );
  var DialogIO = require( 'SUN/DialogIO' );

  // strings
  var closeString = SunA11yStrings.close.value;

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   * @constructor
   */
  function Dialog( content, options ) {

    var self = this;

    options = _.extend( {

      // Dialog-specific options
      modal: true, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center
      titleSpacing: 20, // {number} how far the title is placed above the content
      hasCloseButton: true, // whether to put a close 'X' button is upper-right corner

      // {function} which sets the dialog's position in global coordinates. called as
      // layoutStrategy( dialog, simBounds, screenBounds, scale )
      layoutStrategy: Dialog.DEFAULT_LAYOUT_STRATEGY,

      // close button options
      closeButtonBaseColor: '#d00',
      closeButtonMargin: 5, // {number} how far away should the close button be from the panel border
      closeButtonListener: function() { self.hide(); },

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      xMargin: 20,
      yMargin: 20,
      tandem: Tandem.optional,
      phetioType: DialogIO,
      phetioReadOnly: false, // default to false so it can pass it through to the close button
      phetioState: false, // default to false so it can pass it through to the close button

      // a11y options
      tagName: 'div',
      ariaRole: 'dialog',
      focusOnCloseNode: null // {Node} receives focus on close, if null focus returns to element that had focus on open
    }, options );

    // @private (read-only)
    this.isModal = options.modal;

    // @private
    this.showCallback = options.showCallback;
    this.hideCallback = options.hideCallback;

    // see https://github.com/phetsims/joist/issues/293
    assert && assert( this.isModal, 'Non-modal dialogs not currently supported' );

    // @private - whether the dialog is showing
    this.isShowing = false;

    var dialogContent = new Node( {
      children: [ content ]
    } );

    if ( options.title ) {

      var titleNode = options.title;
      dialogContent.addChild( titleNode );

      var updateTitlePosition = function() {
        switch( options.titleAlign ) {
          case 'center':
            titleNode.centerX = content.centerX;
            break;
          case 'left':
            titleNode.left = content.left;
            break;
          case 'right':
            titleNode.right = content.right;
            break;
          default:
            throw new Error( 'unknown titleAlign for Dialog: ' + options.titleAlign );
        }
        titleNode.bottom = content.top - options.titleSpacing;
      };

      if ( options.resize ) {
        content.on( 'bounds', updateTitlePosition );
        titleNode.on( 'localBounds', updateTitlePosition );
      }
      updateTitlePosition();
    }

    Panel.call( this, dialogContent, options );

    if ( options.hasCloseButton ) {

      var crossSize = 10;
      var crossNode = new Path( new Shape().moveTo( 0, 0 ).lineTo( crossSize, crossSize ).moveTo( 0, crossSize ).lineTo( crossSize, 0 ), {
        stroke: '#fff',
        lineWidth: 3
      } );

      var closeButton = new RectangularPushButton( {
        content: crossNode,
        baseColor: options.closeButtonBaseColor,
        xMargin: 5,
        yMargin: 5,
        listener: options.closeButtonListener,
        accessibleFire: function() {
          self.focusActiveElement();
        },
        tandem: options.tandem.createTandem( 'closeButton' ),
        phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog
        phetioState: options.phetioState, // match the state transfer of the Dialog

        // a11y options
        tagName: 'button',
        innerContent: closeString
      } );
      this.addChild( closeButton );

      var updateClosePosition = function() {
        closeButton.right = dialogContent.right + options.xMargin - options.closeButtonMargin;
        closeButton.top = dialogContent.top - options.yMargin + options.closeButtonMargin;
      };

      if ( options.resize ) {
        dialogContent.on( 'bounds', updateClosePosition );
        if ( options.title ) {
          options.title.on( 'bounds', updateClosePosition );
        }
      }
      updateClosePosition();
    }

    var sim = window.phet.joist.sim;

    // @private
    this.updateLayout = function() {
      options.layoutStrategy( self, sim.boundsProperty.value, sim.screenBoundsProperty.value, sim.scaleProperty.value );
    };

    this.updateLayout();

    // @private
    this.sim = sim;

    // a11y - set the order of content for accessibility, title before content
    this.accessibleOrder = [ titleNode, dialogContent ];

    // a11y - set the aria labelledby and describedby relations so that whenever focus enters the dialog, the title
    // and description content are read in full
    content.tagName && this.setAriaDescribedByNode( content );
    if ( options.title ) {
      options.title.tagName && this.setAriaLabelledByNode( options.title );
    }

    // must be removed on dispose
    this.sim.resizedEmitter.addListener( this.updateLayout );

    // @private (a11y) - the active element when the dialog is shown, tracked so that focus can be restored on close
    this.activeElement = options.focusOnCloseNode || null;

    // a11y - close the dialog when pressing "escape"
    var escapeListener = {
      keydown: function( event ) {
        if ( event.keyCode === KeyboardUtil.KEY_ESCAPE ) {
          event.preventDefault();
          self.hide();
          self.focusActiveElement();
        }
        else if ( event.keyCode === KeyboardUtil.KEY_TAB && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          var activeId = Display.focus.trail.getUniqueId();
          var noNextFocusable = AccessibilityUtil.getNextFocusable().id === activeId;
          var noPreviousFocusable = AccessibilityUtil.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            event.preventDefault();
          }
        }
      }
    };
    this.addAccessibleInputListener( escapeListener );

    // @private - to be called on dispose()
    this.disposeDialog = function() {
      self.sim.resizedEmitter.removeListener( self.updateLayout );
      self.removeAccessibleInputListener( escapeListener );

      if ( options.hasCloseButton ) {
        closeButton.dispose();

        if ( options.resize ) {
          dialogContent.off( 'bounds', updateClosePosition );
          if ( options.title ) {
            options.title.off( 'bounds', updateClosePosition );
            titleNode.off( 'localBounds', updateTitlePosition );
            content.off( 'bounds', updateTitlePosition );
          }
        }
      }

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  sun.register( 'Dialog', Dialog );

  // @private
  Dialog.DEFAULT_LAYOUT_STRATEGY = function( dialog, simBounds, screenBounds, scale ) {

    // The size is set in the Sim.topLayer, but we need to update the location here
    dialog.center = simBounds.center.times( 1.0 / scale );
  };

  return inherit( Panel, Dialog, {

    // @public
    show: function() {
      if ( !this.isShowing ) {
        window.phet.joist.sim.showPopup( this, this.isModal );
        this.isShowing = true;

        // a11y - store the currently active element before hiding all other accessible content
        // so that the active element isn't blurred
        this.activeElement = this.activeElement || Display.focusedNode;
        this.setAccessibleViewsVisible( false );

        // In case the window size has changed since the dialog was hidden, we should try layout out again.
        // See https://github.com/phetsims/joist/issues/362
        this.updateLayout();

        // Do this last
        this.showCallback && this.showCallback();
      }
    },

    /**
     * Hide the dialog.  If you create a new dialog next time you show(), be sure to dispose this
     * dialog instead.
     * @public
     */
    hide: function() {
      if ( this.isShowing ) {

        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;

        // a11y - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.setAccessibleViewsVisible( true );

        // Do this last
        this.hideCallback && this.hideCallback();
      }
    },

    /**
     * Make eligible for garbage collection.
     * @public
     */
    dispose: function() {
      this.hide();
      this.disposeDialog();
      Panel.prototype.dispose.call( this );
    },

    /**
     * Hide or show all accessible content related to the sim ScreenViews, navigation bar, and alert content. Instead
     * of using setVisible, we have to remove the subtree of accessible content from each view element in order to
     * prevent an IE11 bug where content remains invisible in the accessibility tree, see
     * https://github.com/phetsims/john-travoltage/issues/247
     *
     * @param {boolean} visible
     */
    setAccessibleViewsVisible: function( visible ) {
      for ( var i = 0; i < this.sim.screens.length; i++ ) {
        this.sim.screens[ i ].view.accessibleContentDisplayed = visible;
      }
      this.sim.navigationBar.accessibleContentDisplayed = visible;
      this.sim.homeScreen && this.sim.homeScreen.view.setAccessibleContentDisplayed( visible );

      // workaround for a strange Edge bug where this child of the navigation bar remains visible,
      // see https://github.com/phetsims/a11y-research/issues/30
      if ( this.sim.navigationBar.keyboardHelpButton ) {
        this.sim.navigationBar.keyboardHelpButton.accessibleVisible = visible;
      }
    },

    /**
     * If there is an active element, focus it.  Should almost always be closed after the Dialog has been closed.
     *
     * @public
     * @a11y
     */
    focusActiveElement: function() {
      this.activeElement && this.activeElement.focus();
    }
  } );
} );<|endoftext|>


<commit_before>/**
 * Heldesks' code (Zendesk, etc..)
 */

App.helpdesk = {
    init: function () {
        // fetch template content from the extension
        if (window.location.hostname.indexOf('zendesk.com') !== -1) {
            App.helpdesk.zendesk.init();
        }
    },
    zendesk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/zendesk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }
            var ticketUrl = "";
            var ticketInterval = setInterval(function () {
                if (window.location.pathname.indexOf('/agent/tickets/') !== -1) {
                    if (!ticketUrl || ticketUrl !== window.location.pathname) {
                        ticketUrl = window.location.pathname;

                        $('.macro-suggestions-container').remove();

                        var bodyInterval = setInterval(function () {
                            var subject = '';
                            var body = '';

                            $('.workspace').each(function (i, w) {
                                if ($(w).css('display') !== 'none') {
                                    subject = $(w).find('input[name=subject]').val();
                                    body = $(w).find('.event-container .zd-comment:first').text();
                                }
                            });

                            if (!body.length) {
                                return;
                            }

                            clearInterval(bodyInterval);

                            chrome.runtime.sendMessage({
                                'request': 'suggestion',
                                'data': {
                                    'agent': {
                                        'host': window.location.host,
                                        'name': $('#face_box .name').text()
                                    },
                                    'subject': subject,
                                    'to': '',
                                    'cc': '',
                                    'bcc': '',
                                    'from': '',
                                    'body': body,
                                    'helpdesk': 'zendesk'
                                }
                            }, function (macros) {
                                if (!_.size(macros)) {
                                    return;
                                }

                                $('.macro-suggestions-container').remove();
                                var macroContainer = $("
");

                                for (var i in macros) {
                                    var macro = macros[i];
                                    var macroBtn = $("");
                                    var macroEl = $("");
                                    var scoreEl = $(' ');
                                    if (macro.score >= 0.8) {
                                        scoreEl.addClass('macro-score-high');
                                    }
                                    if (macro.score >= 0.6 && macro.score < 0.8) {
                                        scoreEl.addClass('macro-score-medium');
                                    }
                                    if (macro.score < 0.6) {
                                        scoreEl.addClass('macro-score-low');
                                    }

                                    macroEl.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                                    macroEl.attr('title', macro.body.replace(/\n/g, "
"));
                                    macroEl.attr('data-toggle', "tooltip");
                                    macroEl.attr('data-html', "true");
                                    macroEl.attr('data-placement', "bottom");
                                    macroEl.html(macro.title);

                                    macroBtn.append(macroEl);
                                    macroBtn.append(scoreEl);
                                    macroContainer.append(macroBtn);
                                }
                                $('.comment_input .content .options').before(macroContainer);
                            });
                        }, 100);
                    }
                } else {
                    ticketUrl = "";
                }
            }, 100);
        }
    }
};

<commit_msg>Don't get suggestion when the last comment was an agents one<commit_after>/**
 * Heldesks' code (Zendesk, etc..)
 */

App.helpdesk = {
    init: function () {
        // fetch template content from the extension
        if (window.location.hostname.indexOf('zendesk.com') !== -1) {
            App.helpdesk.zendesk.init();
        }
    },
    zendesk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/zendesk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }
            var ticketUrl = "";
            var ticketInterval = setInterval(function () {
                if (window.location.pathname.indexOf('/agent/tickets/') !== -1) {
                    if (!ticketUrl || ticketUrl !== window.location.pathname) {
                        ticketUrl = window.location.pathname;

                        $('.macro-suggestions-container').remove();

                        var bodyInterval = setInterval(function () {
                            var subject = '';
                            var body = '';

                            $('.workspace').each(function (i, workspace) {
                                workspace = $(workspace);

                                if (workspace.css('display') !== 'none') {
                                    var firstEvent = workspace.find('.event-container .event:first');
                                    var isAgent = firstEvent.find('.user_photo').hasClass('agent');

                                    // If it's an agent who has the last comment no point in suggesting anything
                                    if (isAgent) {
                                        return false;
                                    }

                                    subject = workspace.find('input[name=subject]').val();
                                    body = firstEvent.find('.zd-comment').text();
                                }
                            });

                            if (!body.length) {
                                return;
                            }

                            clearInterval(bodyInterval);

                            chrome.runtime.sendMessage({
                                'request': 'suggestion',
                                'data': {
                                    'agent': {
                                        'host': window.location.host,
                                        'name': $('#face_box .name').text()
                                    },
                                    'subject': subject,
                                    'to': '',
                                    'cc': '',
                                    'bcc': '',
                                    'from': '',
                                    'body': body,
                                    'helpdesk': 'zendesk'
                                }
                            }, function (macros) {
                                if (!_.size(macros)) {
                                    return;
                                }

                                $('.macro-suggestions-container').remove();
                                var macroContainer = $("
");

                                for (var i in macros) {
                                    var macro = macros[i];
                                    var macroBtn = $("");
                                    var macroEl = $("");
                                    var scoreEl = $(' ');
                                    if (macro.score >= 0.8) {
                                        scoreEl.addClass('macro-score-high');
                                    }
                                    if (macro.score >= 0.6 && macro.score < 0.8) {
                                        scoreEl.addClass('macro-score-medium');
                                    }
                                    if (macro.score < 0.6) {
                                        scoreEl.addClass('macro-score-low');
                                    }

                                    macroEl.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                                    macroBtn.attr('title', macro.body.replace(/\n/g, "
"));
                                    macroBtn.attr('data-toggle', "tooltip");
                                    macroBtn.attr('data-html', "true");
                                    macroBtn.attr('data-placement', "bottom");
                                    macroEl.html(macro.title);

                                    macroBtn.append(macroEl);
                                    macroBtn.append(scoreEl);
                                    macroContainer.append(macroBtn);
                                }
                                $('.comment_input .content .options').before(macroContainer);
                            });
                        }, 100);
                    }
                } else {
                    ticketUrl = "";
                }
            }, 100);
        }
    }
};

<|endoftext|>


<commit_before>/**
 * Heldesks' code (Zendesk, Desk etc..)
 */

App.helpdesk = {
    init: function () {
        // fetch template content from the extension
        if (window.location.hostname.indexOf('.zendesk.com') !== -1) {
            App.helpdesk.zendesk.init();
        }
        // for desk.com it's more complicated as there are custom domains
        if ($('link[href^=https\\:\\/\\/cdn\\.desk\\.com]').length) {
            App.helpdesk.desk.init();
        }
    },
    zendesk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/zendesk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }

            // forward the message to the
            window.addEventListener('message', function (event) {
                if (event.data && event.data.request && event.data.request === 'suggestion-used') {
                    chrome.runtime.sendMessage({
                        'request': 'suggestion-used',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#face_box .name').text()
                            },
                            'url': window.location.href,
                            'template_id': event.data.template_id
                        }
                    });
                }
            });

            var ticketUrl = "";
            var ticketCheck = function () {
                if (window.location.pathname.indexOf('/agent/tickets/') === -1) {
                    // reset the ticket url if we're not inside a ticket anymore
                    ticketUrl = "";
                    return;
                }
                // don't fetch for the same ticket
                if (ticketUrl === window.location.pathname) {
                    return;
                }

                ticketUrl = window.location.pathname;

                $('.macro-suggestions-container').remove();

                var bodyCheck = function () {
                    var subject = '';
                    var body = '';

                    $('.workspace').each(function (i, workspace) {
                        workspace = $(workspace);

                        if (workspace.css('display') !== 'none') {
                            var firstEvent = workspace.find('.event-container .event.is-public:first');
                            var isAgent = firstEvent.find('.user_photo').hasClass('agent');

                            // If it's an agent who has the last comment no point in suggesting anything
                            if (isAgent) {
                                return false;
                            }

                            subject = workspace.find('input[name=subject]').val();
                            body = firstEvent.find('.zd-comment').text();
                        }
                    });

                    if (!subject || !subject.length || !body.length) {
                        return;
                    }
                    clearInterval(bodyInterval);

                    chrome.runtime.sendMessage({
                        'request': 'suggestion',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#face_box .name').text()
                            },
                            'url': window.location.href,
                            'subject': subject,
                            'to': '',
                            'cc': '',
                            'bcc': '',
                            'from': '',
                            'body': body,
                            'helpdesk': 'zendesk'
                        }
                    }, function (macros) {
                        if (!_.size(macros)) {
                            return;
                        }

                        $('.macro-suggestions-container').remove();
                        var macroContainer = $("
");

                        for (var i in macros) {
                            var macro = macros[i];
                            var macroBtn = $("");
                            var macroEl = $("");
                            /*
                             var scoreEl = $(' ');
                             if (macro.score >= 0.9) {
                             scoreEl.addClass('macro-score-high');
                             }
                             if (macro.score >= 0.7 && macro.score < 0.9) {
                             scoreEl.addClass('macro-score-medium');
                             }
                             if (macro.score < 0.7) {
                             scoreEl.addClass('macro-score-low');
                             }
                             */

                            macroBtn.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                            macroBtn.attr('title', macro.body.replace(/\n/g, "
"));
                            macroBtn.attr('data-toggle', "tooltip");
                            macroBtn.attr('data-html', "true");
                            macroBtn.attr('data-placement', "bottom");
                            macroEl.html(macro.title);

                            macroBtn.append(macroEl);
                            //macroBtn.append(scoreEl);
                            macroContainer.append(macroBtn);
                        }
                        $('.comment_input .content .options').before(macroContainer);
                    });
                };
                var bodyInterval = setInterval(bodyCheck, 200);
            };
            var ticketInterval = setInterval(ticketCheck, 200);
        }
    },
    desk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/desk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }

            // forward the message to the
            window.addEventListener('message', function (event) {
                if (event.data && event.data.request && event.data.request === 'suggestion-used') {
                    chrome.runtime.sendMessage({
                        'request': 'suggestion-used',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#systembar-user-settings-nav>.a-gravatar>span').text()
                            },
                            'url': window.location.href,
                            'template_id': event.data.template_id
                        }
                    });
                }
            });

            // check if we are in a ticket at regular intervals
            var ticketId = '';
            var caseCheck = function () {
                var activeTicket = $('.active_ticket_type:not(.ui-tabs-hide)');

                // if we're on the same ticket no need to fetch the data again
                if (!activeTicket.length || ticketId === activeTicket.attr('id')) {
                    return;
                }

                ticketId = activeTicket.attr('id');

                var lastComment = activeTicket.find('.customer_history_ticket_container:not(.customer_history_ticket_outside_note):last');
                var isAgent = lastComment.find('.a-user-icon').hasClass('agent_thumb_50');
                if (isAgent) { //last comment by the agent
                    return;
                }

                var subject = activeTicket.find('.a-ticket-subject').text().trim();
                var body = lastComment.find('.a-ticket-meat').text();

                if (!subject || !subject.length || !body.length) {
                    return;
                }

                chrome.runtime.sendMessage({
                    'request': 'suggestion',
                    'data': {
                        'agent': {
                            'host': window.location.host,
                            'name': $('#systembar-user-settings-nav>.a-gravatar>span').text()
                        },
                        'url': activeTicket.find('input[name^=direct_link]').val(),
                        'subject': subject,
                        'to': '',
                        'cc': '',
                        'bcc': '',
                        'from': '',
                        'body': body,
                        'helpdesk': 'desk'
                    }
                }, function (macros) {
                    if (!_.size(macros)) {
                        return;
                    }

                    $('.macro-suggestions-container').remove();
                    var macroContainer = $("
");
                    macroContainer.css('margin-left', '63px');

                    for (var i in macros) {
                        var macro = macros[i];
                        var macroBtn = $("<button class='macro-suggestion a-grey-button'>");
                        var macroEl = $("");

                        macroBtn.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                        macroBtn.attr('title', macro.body);
                        macroEl.html(macro.title);

                        macroBtn.append(macroEl);
                        macroContainer.append(macroBtn);
                    }
                    activeTicket.find('.a-ticket-reply form:first').after(macroContainer);
                });
            };
            var caseInterval = setInterval(caseCheck, 200);
        }
    }
};
<commit_msg>Fixed reporting when macro was used for desk.com<commit_after>/**
 * Heldesks' code (Zendesk, Desk etc..)
 */

App.helpdesk = {
    init: function () {
        // fetch template content from the extension
        if (window.location.hostname.indexOf('.zendesk.com') !== -1) {
            App.helpdesk.zendesk.init();
        }
        // for desk.com it's more complicated as there are custom domains
        if ($('link[href^=https\\:\\/\\/cdn\\.desk\\.com]').length) {
            App.helpdesk.desk.init();
        }
    },
    zendesk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/zendesk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }

            // forward the message to the
            window.addEventListener('message', function (event) {
                if (event.data && event.data.request && event.data.request === 'suggestion-used') {
                    chrome.runtime.sendMessage({
                        'request': 'suggestion-used',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#face_box .name').text()
                            },
                            'url': window.location.href,
                            'template_id': event.data.template_id
                        }
                    });
                }
            });

            var ticketUrl = "";
            var ticketCheck = function () {
                if (window.location.pathname.indexOf('/agent/tickets/') === -1) {
                    // reset the ticket url if we're not inside a ticket anymore
                    ticketUrl = "";
                    return;
                }
                // don't fetch for the same ticket
                if (ticketUrl === window.location.pathname) {
                    return;
                }

                ticketUrl = window.location.pathname;

                $('.macro-suggestions-container').remove();

                var bodyCheck = function () {
                    var subject = '';
                    var body = '';

                    $('.workspace').each(function (i, workspace) {
                        workspace = $(workspace);

                        if (workspace.css('display') !== 'none') {
                            var firstEvent = workspace.find('.event-container .event.is-public:first');
                            var isAgent = firstEvent.find('.user_photo').hasClass('agent');

                            // If it's an agent who has the last comment no point in suggesting anything
                            if (isAgent) {
                                return false;
                            }

                            subject = workspace.find('input[name=subject]').val();
                            body = firstEvent.find('.zd-comment').text();
                        }
                    });

                    if (!subject || !subject.length || !body.length) {
                        return;
                    }
                    clearInterval(bodyInterval);

                    chrome.runtime.sendMessage({
                        'request': 'suggestion',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#face_box .name').text()
                            },
                            'url': window.location.href,
                            'subject': subject,
                            'to': '',
                            'cc': '',
                            'bcc': '',
                            'from': '',
                            'body': body,
                            'helpdesk': 'zendesk'
                        }
                    }, function (macros) {
                        if (!_.size(macros)) {
                            return;
                        }

                        $('.macro-suggestions-container').remove();
                        var macroContainer = $("
");

                        for (var i in macros) {
                            var macro = macros[i];
                            var macroBtn = $("");
                            var macroEl = $("");
                            /*
                             var scoreEl = $(' ');
                             if (macro.score >= 0.9) {
                             scoreEl.addClass('macro-score-high');
                             }
                             if (macro.score >= 0.7 && macro.score < 0.9) {
                             scoreEl.addClass('macro-score-medium');
                             }
                             if (macro.score < 0.7) {
                             scoreEl.addClass('macro-score-low');
                             }
                             */

                            macroBtn.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                            macroBtn.attr('title', macro.body.replace(/\n/g, "
"));
                            macroBtn.attr('data-toggle', "tooltip");
                            macroBtn.attr('data-html', "true");
                            macroBtn.attr('data-placement', "bottom");
                            macroEl.html(macro.title);

                            macroBtn.append(macroEl);
                            //macroBtn.append(scoreEl);
                            macroContainer.append(macroBtn);
                        }
                        $('.comment_input .content .options').before(macroContainer);
                    });
                };
                var bodyInterval = setInterval(bodyCheck, 200);
            };
            var ticketInterval = setInterval(ticketCheck, 200);
        }
    },
    desk: {
        init: function () {
            // inject the zendesk script into the dom
            var script = document.createElement('script');
            script.type = "text/javascript";
            script.src = chrome.extension.getURL("pages/helpdesk/desk.js");
            if (document.body) {
                document.body.appendChild(script);
                script.onload = function () {
                    document.body.removeChild(script);
                };
            }

            // forward the message to the
            window.addEventListener('message', function (event) {
                if (event.data && event.data.request && event.data.request === 'suggestion-used') {
                    var activeTicket = $('.active_ticket_type:not(.ui-tabs-hide)');
                    chrome.runtime.sendMessage({
                        'request': 'suggestion-used',
                        'data': {
                            'agent': {
                                'host': window.location.host,
                                'name': $('#systembar-user-settings-nav>.a-gravatar>span').text()
                            },
                            'url': activeTicket.find('input[name^=direct_link]').val(),
                            'template_id': event.data.template_id
                        }
                    });
                }
            });

            // check if we are in a ticket at regular intervals
            var ticketId = '';
            var caseCheck = function () {
                var activeTicket = $('.active_ticket_type:not(.ui-tabs-hide)');

                // if we're on the same ticket no need to fetch the data again
                if (!activeTicket.length || ticketId === activeTicket.attr('id')) {
                    return;
                }

                ticketId = activeTicket.attr('id');

                var lastComment = activeTicket.find('.customer_history_ticket_container:not(.customer_history_ticket_outside_note):last');
                var isAgent = lastComment.find('.a-user-icon').hasClass('agent_thumb_50');
                if (isAgent) { //last comment by the agent
                    return;
                }

                var subject = activeTicket.find('.a-ticket-subject').text().trim();
                var body = lastComment.find('.a-ticket-meat').text();

                if (!subject || !subject.length || !body.length) {
                    return;
                }

                chrome.runtime.sendMessage({
                    'request': 'suggestion',
                    'data': {
                        'agent': {
                            'host': window.location.host,
                            'name': $('#systembar-user-settings-nav>.a-gravatar>span').text()
                        },
                        'url': activeTicket.find('input[name^=direct_link]').val(),
                        'subject': subject,
                        'to': '',
                        'cc': '',
                        'bcc': '',
                        'from': '',
                        'body': body,
                        'helpdesk': 'desk'
                    }
                }, function (macros) {
                    if (!_.size(macros)) {
                        return;
                    }

                    $('.macro-suggestions-container').remove();
                    var macroContainer = $("
");
                    macroContainer.css('margin-left', '63px');

                    for (var i in macros) {
                        var macro = macros[i];
                        var macroBtn = $("<button class='macro-suggestion a-grey-button'>");
                        var macroEl = $("");

                        macroBtn.attr('onclick', "gorgiasApplyMacroSuggestion(" + macro["external_id"] + ")");
                        macroBtn.attr('title', macro.body);
                        macroEl.html(macro.title);

                        macroBtn.append(macroEl);
                        macroContainer.append(macroBtn);
                    }
                    activeTicket.find('.a-ticket-reply form:first').after(macroContainer);
                });
            };
            var caseInterval = setInterval(caseCheck, 200);
        }
    }
};
<|endoftext|>


<commit_before>'use strict';

module.exports = function(grunt) {

    grunt.registerTask('test:e2e', [
        'clean:reports',
        'clean:server',
        'concurrent:server',
        'postcss:css',
        'instrument',
        'browserSync:test',
        'shell:jasmine2'
    ]);

    grunt.registerTask('test:unit', [
        'clean:reports',
        'karma:continuous'
    ]);

    grunt.registerTask('test:dev:unit', [
        'clean:reports',
        'karma:unit'
    ]);

    grunt.registerTask('test', [
        'clean:reports',
        'karma:continuous',
        'clean:server',
        'concurrent:server',
        'postcss:css',
        'instrument',
        'browserSync:test',
        'shell:jasmine2'
    ]);

    grunt.registerTask('test:unit:auto', [
        'karma:unit_auto'
    ]);

};
<commit_msg>Added missing wiredep and includeSource to test tasks<commit_after>'use strict';

module.exports = function(grunt) {

    grunt.registerTask('test:e2e', [
        'clean:reports',
        'clean:server',
        'concurrent:server',
        'postcss:css',
        'wiredep',
        'includeSource',
        'instrument',
        'browserSync:test',
        'shell:jasmine2'
    ]);

    grunt.registerTask('test:unit', [
        'clean:reports',
        'karma:continuous'
    ]);

    grunt.registerTask('test:dev:unit', [
        'clean:reports',
        'karma:unit'
    ]);

    grunt.registerTask('test', [
        'clean:reports',
        'karma:continuous',
        'clean:server',
        'concurrent:server',
        'postcss:css',
        'wiredep',
        'includeSource',
        'instrument',
        'browserSync:test',
        'shell:jasmine2'
    ]);

    grunt.registerTask('test:unit:auto', [
        'karma:unit_auto'
    ]);

};<|endoftext|>