<commit_before>// --
// Core.AJAX.UnitTest.js - UnitTests
// Copyright (C) 2001-2010 OTRS AG, http://otrs.org/\n";
// --
// $Id: Core.AJAX.UnitTest.js,v 1.3 2010-12-09 17:12:43 mg Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var OTRS = OTRS || {};
Core.AJAX = Core.AJAX || {};

Core.AJAX = (function (Namespace) {
    Namespace.RunUnitTests = function(){
        module('Core.AJAX');
        test('Core.AJAX.SerializeForm()', function(){

            var Tests =
            [
                 {
                     HTML:   '',
                     Result: '',
                     Name:   'Empty form.'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" checked="checked"/>',
                     Result: '',
                     Name:   'Checkbox without name'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element" checked="checked"/>',
                     Result: 'Element=test;',
                     Name:   'Checkbox with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element@" checked="checked"/>',
                     Result: 'Element%40=test;',
                     Name:   'Checkbox funky name, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" name="Element" checked="checked"/>',
                     Result: 'Element=on;',
                     Name:   'Checkbox with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" name="Element" value="test" checked="checked"/><input type="checkbox" name="Element" value="test2" checked="checked"/><input type="checkbox" name="Element" value="test3"/>',
                     Result: 'Element=test;Element=test2;',
                     Name:   'Multiple checkboxes with the same name'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element"/>',
                     Result: '',
                     Name:   'Checkbox with custom value, unchecked'

                 },
                 {
                     HTML:   '<input type="radio" value="test" name="Element" checked="checked"/>',
                     Result: 'Element=test;',
                     Name:   'Radio with custom value, checked'

                 },
                 {
                     HTML:   '<input type="radio" value="test" name="Element" /><input type="radio" value="test2" name="Element" checked="checked"/><input type="radio" value="test3" name="Element"/>',
                     Result: 'Element=test2;',
                     Name:   'Radio with custom value, checked'

                 },
                 {
                     HTML:   '<input type="Radio" name="Element" checked="checked"/>',
                     Result: 'Element=on;',
                     Name:   'Multiple radios with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element"/>',
                     Result: '',
                     Name:   'Radio with custom value, unchecked'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1" selected="selected">one</option><option value="2" selected="selected">two</option></select>',
                     Result: 'Element=1%2C2;',
                     Name:   'Multi-select with multiple values'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1" selected="selected">one</option><option value="2">two</option></select>',
                     Result: 'Element=1;',
                     Name:   'Multi-Select with one value'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1">one</option><option value="2">two</option></select>',
                     Result: 'Element=;',
                     Name:   'Multi-select, empty'

                 },
                 {
                     HTML:   '<select name="Element"><option value="1" selected="selected">one</option><option value="2" selected="selected">two</option></select>',
                     Result: 'Element=2;',
                     Name:   'Select with one value but multiple preselected'

                 },
                 {
                     HTML:   '<select name="Element"><option value="1">one</option><option value="2">two</option></select>',
                     Result: 'Element=1;',
                     Name:   'Select, empty'

                 },
                 {
                     HTML:   '<input type="text" name="Element"/>',
                     Result: 'Element=;',
                     Name:   'Textarea, empty'

                 },
                 {
                     HTML:   '<input type="text" name="Element"/>',
                     Result: 'Element=;',
                     Name:   'Textarea, empty with value attribute'

                 },
                 {
                     HTML:   '<input type="text" value="test" name="Element"/>',
                     Result: 'Element=test;',
                     Name:   'Textarea, simple'

                 },
                 {
                     HTML:   '<textarea name="Element">1\n2</textarea>',
                     Result: 'Element=1%0A2;',
                     Name:   'Textarea, with newline'

                 },
                 {
                     HTML:   '<textarea name="Element">1\n2</textarea><textarea name="Element2">1\n2</textarea>',
                     Ignore: { Element: 1 },
                     Result: 'Element2=1%0A2;',
                     Name:   'Textarea, test Ignore parameter'

                 }

            ];

            expect(Tests.length);

            /*
             * Create a form containter for the tests
             */
            $('body').append('<form id="CORE_AJAX_TestForm1"></form>');

            /*
             * Run the tests
             */
            $.each(Tests, function(){
                $('#CORE_AJAX_TestForm1').empty().append(this.HTML);
                equals(Core.AJAX.SerializeForm($('#CORE_AJAX_TestForm1'), this.Ignore), this.Result, this.Name);

            });

            /*
             * Cleanup form container and contents
             */
            $('#CORE_AJAX_TestForm1').remove();
        });
    };

    return Namespace;
}(Core.AJAX || {}));<commit_msg>Added test for hidden field.<commit_after>// --
// Core.AJAX.UnitTest.js - UnitTests
// Copyright (C) 2001-2010 OTRS AG, http://otrs.org/\n";
// --
// $Id: Core.AJAX.UnitTest.js,v 1.4 2010-12-10 09:37:12 mg Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var OTRS = OTRS || {};
Core.AJAX = Core.AJAX || {};

Core.AJAX = (function (Namespace) {
    Namespace.RunUnitTests = function(){
        module('Core.AJAX');
        test('Core.AJAX.SerializeForm()', function(){

            var Tests =
            [
                 {
                     HTML:   '',
                     Result: '',
                     Name:   'Empty form.'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" checked="checked"/>',
                     Result: '',
                     Name:   'Checkbox without name'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element" checked="checked"/>',
                     Result: 'Element=test;',
                     Name:   'Checkbox with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element@" checked="checked"/>',
                     Result: 'Element%40=test;',
                     Name:   'Checkbox funky name, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" name="Element" checked="checked"/>',
                     Result: 'Element=on;',
                     Name:   'Checkbox with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" name="Element" value="test" checked="checked"/><input type="checkbox" name="Element" value="test2" checked="checked"/><input type="checkbox" name="Element" value="test3"/>',
                     Result: 'Element=test;Element=test2;',
                     Name:   'Multiple checkboxes with the same name'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element"/>',
                     Result: '',
                     Name:   'Checkbox with custom value, unchecked'

                 },
                 {
                     HTML:   '<input type="radio" value="test" name="Element" checked="checked"/>',
                     Result: 'Element=test;',
                     Name:   'Radio with custom value, checked'

                 },
                 {
                     HTML:   '<input type="radio" value="test" name="Element" /><input type="radio" value="test2" name="Element" checked="checked"/><input type="radio" value="test3" name="Element"/>',
                     Result: 'Element=test2;',
                     Name:   'Radio with custom value, checked'

                 },
                 {
                     HTML:   '<input type="Radio" name="Element" checked="checked"/>',
                     Result: 'Element=on;',
                     Name:   'Multiple radios with custom value, checked'

                 },
                 {
                     HTML:   '<input type="checkbox" value="test" name="Element"/>',
                     Result: '',
                     Name:   'Radio with custom value, unchecked'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1" selected="selected">one</option><option value="2" selected="selected">two</option></select>',
                     Result: 'Element=1%2C2;',
                     Name:   'Multi-select with multiple values'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1" selected="selected">one</option><option value="2">two</option></select>',
                     Result: 'Element=1;',
                     Name:   'Multi-Select with one value'

                 },
                 {
                     HTML:   '<select name="Element" multiple="multiple"><option value="1">one</option><option value="2">two</option></select>',
                     Result: 'Element=;',
                     Name:   'Multi-select, empty'

                 },
                 {
                     HTML:   '<select name="Element"><option value="1" selected="selected">one</option><option value="2" selected="selected">two</option></select>',
                     Result: 'Element=2;',
                     Name:   'Select with one value but multiple preselected'

                 },
                 {
                     HTML:   '<select name="Element"><option value="1">one</option><option value="2">two</option></select>',
                     Result: 'Element=1;',
                     Name:   'Select, empty'

                 },
                 {
                     HTML:   '<input type="text" name="Element"/>',
                     Result: 'Element=;',
                     Name:   'Textarea, empty'

                 },
                 {
                     HTML:   '<input type="text" name="Element"/>',
                     Result: 'Element=;',
                     Name:   'Textarea, empty with value attribute'

                 },
                 {
                     HTML:   '<input type="text" value="test" name="Element"/>',
                     Result: 'Element=test;',
                     Name:   'Textarea, simple'

                 },
                 {
                     HTML:   '<textarea name="Element">1\n2</textarea>',
                     Result: 'Element=1%0A2;',
                     Name:   'Textarea, with newline'

                 },
                 {
                     HTML:   '<textarea name="Element">1\n2</textarea><textarea name="Element2">1\n2</textarea>',
                     Ignore: { Element: 1 },
                     Result: 'Element2=1%0A2;',
                     Name:   'Textarea, test Ignore parameter'

                 },
                 {
                     HTML:   '<input type="hidden" name="Element" value="12 3"/>',
                     Result: 'Element=12%203;',
                     Name:   'Hidden field'

                 }

            ];

            expect(Tests.length);

            /*
             * Create a form containter for the tests
             */
            $('body').append('<form id="CORE_AJAX_TestForm1"></form>');

            /*
             * Run the tests
             */
            $.each(Tests, function(){
                $('#CORE_AJAX_TestForm1').empty().append(this.HTML);
                equals(Core.AJAX.SerializeForm($('#CORE_AJAX_TestForm1'), this.Ignore), this.Result, this.Name);

            });

            /*
             * Cleanup form container and contents
             */
            $('#CORE_AJAX_TestForm1').remove();
        });
    };

    return Namespace;
}(Core.AJAX || {}));<|endoftext|>


<commit_before>/**
 * @file play command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

const yt = require('youtube-dl');
const jsonDB = require('node-json-db');
const db = new jsonDB('./data/playlist', true, true);

exports.exec = (Bastion, message, args) => {
  // TODO: Auto pause/resume playback
  if (message.guild.music.textChannelID) {
    if (message.channel.id !== message.guild.music.textChannelID) return;
  }

  if (!args.song && !args.ytpl && !args.playlist) {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  let voiceChannel, textChannel, vcStats;
  if (message.guild.voiceConnection) {
    voiceChannel = message.guild.voiceConnection.channel;
    textChannel = message.channel;
    vcStats = Bastion.strings.error(message.guild.language, 'userNoSameVC', true, message.author.tag);
  }
  else if (message.guild.music.textChannelID && message.guild.music.voiceChannelID) {
    if (!(voiceChannel = message.guild.channels.filter(c => c.type === 'voice').get(message.guild.music.voiceChannelID)) || !(textChannel = message.guild.channels.filter(c => c.type === 'text').get(message.guild.music.textChannelID))) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'invalidInput'), Bastion.strings.error(message.guild.language, 'invalidMusicChannel', true), message.channel);
    }
    if (!voiceChannel.joinable) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'noPermission', true, 'join', voiceChannel.name), message.channel);
    }
    if (!voiceChannel.speakable) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'noPermission', true, 'speak', `in ${voiceChannel.name}`), message.channel);
    }
    vcStats = Bastion.strings.error(message.guild.language, 'userNoMusicChannel', true, message.author.tag, voiceChannel.name);
  }
  else {
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'musicChannelNotFound', true), message.channel);
  }

  if (textChannel.id !== message.channel.id) return;
  if (voiceChannel.members.get(message.author.id) === undefined) {
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', '', vcStats, message.channel);
  }

  message.guild.music.voiceChannel = voiceChannel;
  message.guild.music.textChannel = textChannel;

  if (!message.guild.music.hasOwnProperty('songs')) {
    message.guild.music.songs = [];
  }
  if (!message.guild.music.hasOwnProperty('playing')) {
    message.guild.music.playing = false;
  }
  if (!message.guild.music.hasOwnProperty('repeat')) {
    message.guild.music.repeat = false;
  }
  if (!message.guild.music.hasOwnProperty('skipVotes')) {
    message.guild.music.skipVotes = [];
  }

  let song = '';
  try {
    if (args.song) {
      song = args.song.join(' ');
    }
    else if (args.ytpl) {
      if (!/^(http[s]?:\/\/)?(www\.)?youtube\.com\/playlist\?list=([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(args.ytpl)) {
        /**
        * Error condition is encountered.
        * @fires error
        */
        return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'invalidInput'), Bastion.strings.error(message.guild.language, 'invalidInput', true, 'YouTube Playlist URL'), textChannel);
      }
      message.channel.send({
        embed: {
          description: 'Processing playlist...'
        }
      }).catch(e => {
        Bastion.log.error(e);
      });

      yt.getInfo(args.ytpl, [ '-q', '-i', '--skip-download', '--no-warnings', '--flat-playlist', '--format=bestaudio[protocol^=http]' ], (err, info) => {
        if (err) {
          Bastion.log.error(err);
          /**
          * Error condition is encountered.
          * @fires error
          */
          return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'connection'), Bastion.strings.error(message.guild.language, 'connection', true), textChannel);
        }
        if (info) {
          if (info.length === 0) {
            /**
            * Error condition is encountered.
            * @fires error
            */
            return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'notFound'), Bastion.strings.error(message.guild.language, 'playlistNotFound', true), textChannel);
          }
          song = info.shift().title;
          message.channel.send({
            embed: {
              description: `Adding ${info.length} songs to the queue...`
            }
          }).catch(e => {
            Bastion.log.error(e);
          });
          // TODO: This executes before `args` is added to the queue, so the first song (`args`) is added later in the queue. Using setTimeout or flags is inefficient, find an efficient way to fix this!
          info.forEach(e => {
            message.guild.music.songs.push({
              url: `https://www.youtube.com/watch?v=${e.url}`,
              id: e.url,
              title: e.title,
              thumbnail: '',
              duration: e.duration,
              requester: message.author.tag
            });
          });
        }
      });
    }
    else if (args.playlist) {
      let playlist;

      db.reload();
      playlist = db.getData('/');
      playlist = playlist[args.playlist.join(' ')];

      if (!playlist || playlist.length === 0) {
        /**
        * Error condition is encountered.
        * @fires error
        */
        return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'notFound'), Bastion.strings.error(message.guild.language, 'notFound', true, 'song/playlist'), textChannel);
      }

      song = playlist.shift();

      message.channel.send({
        embed: {
          description: `Adding ${playlist.length + 1} favourite songs to the queue...`
        }
      }).catch(e => {
        Bastion.log.error(e);
      });

      // TODO: This executes before `args` is added to the queue, so the first song (`args`) is added later in the queue. Using setTimeout or flags is inefficient, find an efficient way to fix this!
      playlist.forEach(e => {
        e = /^(http[s]?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(e) ? e : `ytsearch:${e}`;
        yt.getInfo(e, [ '-q', '-i', '--skip-download', '--no-warnings', '--format=bestaudio[protocol^=http]' ], (err, info) => {
          if (err || info.format_id === undefined || info.format_id.startsWith('0')) return;
          message.guild.music.songs.push({
            url: info.formats[info.formats.length - 1].url,
            id: info.id,
            title: info.title,
            thumbnail: info.thumbnail,
            duration: info.duration,
            requester: message.author.tag
          });
        });
      });
    }
    else return;

    song = /^(http[s]?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(song) ? song : `ytsearch:${song}`;

    yt.getInfo(song, [ '-q', '-i', '--skip-download', '--no-warnings', '--format=bestaudio[protocol^=http]' ], (err, info) => {
      if (err || info.format_id === undefined || info.format_id.startsWith('0')) {
        return message.channel.send({
          embed: {
            color: Bastion.colors.RED,
            description: Bastion.strings.error(message.guild.language, 'notFound', true, 'result')
          }
        }).catch(e => {
          Bastion.log.error(e);
        });
      }

      message.guild.music.songs.push({
        url: info.formats[info.formats.length - 1].url,
        id: info.id,
        title: info.title,
        thumbnail: info.thumbnail,
        duration: info.duration,
        requester: message.author.tag
      });
      textChannel.send({
        embed: {
          color: Bastion.colors.GREEN,
          title: 'Added to the queue',
          url: info.id ? `https://youtu.be/${info.id}` : '',
          description: info.title,
          thumbnail: {
            url: info.thumbnail
          },
          footer: {
            text: `Position: ${message.guild.music.songs.length} | Duration: ${info.duration || 'N/A'} | Requester: ${message.author.tag}`
          }
        }
      }).catch(e => {
        Bastion.log.error(e);
      });
      if (message.guild.music && message.guild.music.playing) return;

      voiceChannel.join().then(connection => {
        message.guild.me.setDeaf(true).catch(() => {});

        startStreamDispatcher(message.guild, connection);
      }).catch(e => {
        Bastion.log.error(e);
      });
    });
  }
  catch (e) {
    Bastion.log.error(e);
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'unknown'), Bastion.strings.error(message.guild.language, 'unknown', true), textChannel);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'song', type: String, multiple: true, defaultOption: true },
    { name: 'ytpl', type: String, alias: 'l' },
    { name: 'playlist', type: String, multiple: true, alias: 'p', defaultValue: [ 'default' ] }
  ]
};

exports.help = {
  name: 'play',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'play < name | song_link | -l <playlist_link> | -p [Playlist Name] >',
  example: [ 'play Shape of you', 'play -l https://www.youtube.com/playlist?list=PL4zQ6RXLMCJx4RD3pyzRX4QYFubtCdn_k', 'play -p My Favs', 'play -p' ]
};

/**
 * Starts a Stream Dispatcher in the specified guild
 * @function startStreamDispatcher
 * @param {Guild} guild the guild object where this command was ran
 * @param {VoiceConnection} connection the VoiceConnection of Bastion in this guild
 * @returns {void}
 */
function startStreamDispatcher(guild, connection) {
  if (guild.music.songs[0] === undefined) {
    return guild.music.textChannel.send({
      embed: {
        color: guild.client.colors.RED,
        description: 'Exiting voice channel.'
      }
    }).then(() => {
      guild.music.dispatcher.end();
      guild.music.voiceChannel.leave();
    }).catch(e => {
      guild.client.log.error(e);
    });
  }

  guild.music.dispatcher = connection.playStream(yt(guild.music.songs[0].url), { passes: 1 });
  guild.music.playing = true;

  guild.music.textChannel.send({
    embed: {
      color: guild.client.colors.BLUE,
      title: 'Playing',
      url: guild.music.songs[0].id ? `https://youtu.be/${guild.music.songs[0].id}` : '',
      description: guild.music.songs[0].title,
      thumbnail: {
        url: guild.music.songs[0].thumbnail
      },
      footer: {
        text: `🔉 ${guild.music.dispatcher.volume * 50}% | Duration: ${guild.music.songs[0].duration || 'N/A'} | Requester: ${guild.music.songs[0].requester}`
      }
    }
  }).catch(e => {
    guild.client.log.error(e);
  });

  guild.music.dispatcher.on('end', () => {
    guild.music.playing = false;
    guild.music.skipVotes = [];
    if (!guild.music.repeat) {
      guild.music.songs.shift();
    }
    else {
      guild.music.repeat = false;
    }
    startStreamDispatcher(guild, connection);
  });

  guild.music.dispatcher.on('error', (err) => {
    guild.music.playing = false;
    guild.music.songs.shift();
    startStreamDispatcher(guild, connection);
    return guild.client.log.error(err);
  });
}
<commit_msg>Set bitrate automatically from VC bitrate - play command<commit_after>/**
 * @file play command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

const yt = require('youtube-dl');
const jsonDB = require('node-json-db');
const db = new jsonDB('./data/playlist', true, true);

exports.exec = (Bastion, message, args) => {
  // TODO: Auto pause/resume playback
  if (message.guild.music.textChannelID) {
    if (message.channel.id !== message.guild.music.textChannelID) return;
  }

  if (!args.song && !args.ytpl && !args.playlist) {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  let voiceChannel, textChannel, vcStats;
  if (message.guild.voiceConnection) {
    voiceChannel = message.guild.voiceConnection.channel;
    textChannel = message.channel;
    vcStats = Bastion.strings.error(message.guild.language, 'userNoSameVC', true, message.author.tag);
  }
  else if (message.guild.music.textChannelID && message.guild.music.voiceChannelID) {
    if (!(voiceChannel = message.guild.channels.filter(c => c.type === 'voice').get(message.guild.music.voiceChannelID)) || !(textChannel = message.guild.channels.filter(c => c.type === 'text').get(message.guild.music.textChannelID))) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'invalidInput'), Bastion.strings.error(message.guild.language, 'invalidMusicChannel', true), message.channel);
    }
    if (!voiceChannel.joinable) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'noPermission', true, 'join', voiceChannel.name), message.channel);
    }
    if (!voiceChannel.speakable) {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'noPermission', true, 'speak', `in ${voiceChannel.name}`), message.channel);
    }
    vcStats = Bastion.strings.error(message.guild.language, 'userNoMusicChannel', true, message.author.tag, voiceChannel.name);
  }
  else {
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'forbidden'), Bastion.strings.error(message.guild.language, 'musicChannelNotFound', true), message.channel);
  }

  if (textChannel.id !== message.channel.id) return;
  if (voiceChannel.members.get(message.author.id) === undefined) {
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', '', vcStats, message.channel);
  }

  message.guild.music.voiceChannel = voiceChannel;
  message.guild.music.textChannel = textChannel;

  if (!message.guild.music.hasOwnProperty('songs')) {
    message.guild.music.songs = [];
  }
  if (!message.guild.music.hasOwnProperty('playing')) {
    message.guild.music.playing = false;
  }
  if (!message.guild.music.hasOwnProperty('repeat')) {
    message.guild.music.repeat = false;
  }
  if (!message.guild.music.hasOwnProperty('skipVotes')) {
    message.guild.music.skipVotes = [];
  }

  let song = '';
  try {
    if (args.song) {
      song = args.song.join(' ');
    }
    else if (args.ytpl) {
      if (!/^(http[s]?:\/\/)?(www\.)?youtube\.com\/playlist\?list=([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(args.ytpl)) {
        /**
        * Error condition is encountered.
        * @fires error
        */
        return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'invalidInput'), Bastion.strings.error(message.guild.language, 'invalidInput', true, 'YouTube Playlist URL'), textChannel);
      }
      message.channel.send({
        embed: {
          description: 'Processing playlist...'
        }
      }).catch(e => {
        Bastion.log.error(e);
      });

      yt.getInfo(args.ytpl, [ '-q', '-i', '--skip-download', '--no-warnings', '--flat-playlist', '--format=bestaudio[protocol^=http]' ], (err, info) => {
        if (err) {
          Bastion.log.error(err);
          /**
          * Error condition is encountered.
          * @fires error
          */
          return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'connection'), Bastion.strings.error(message.guild.language, 'connection', true), textChannel);
        }
        if (info) {
          if (info.length === 0) {
            /**
            * Error condition is encountered.
            * @fires error
            */
            return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'notFound'), Bastion.strings.error(message.guild.language, 'playlistNotFound', true), textChannel);
          }
          song = info.shift().title;
          message.channel.send({
            embed: {
              description: `Adding ${info.length} songs to the queue...`
            }
          }).catch(e => {
            Bastion.log.error(e);
          });
          // TODO: This executes before `args` is added to the queue, so the first song (`args`) is added later in the queue. Using setTimeout or flags is inefficient, find an efficient way to fix this!
          info.forEach(e => {
            message.guild.music.songs.push({
              url: `https://www.youtube.com/watch?v=${e.url}`,
              id: e.url,
              title: e.title,
              thumbnail: '',
              duration: e.duration,
              requester: message.author.tag
            });
          });
        }
      });
    }
    else if (args.playlist) {
      let playlist;

      db.reload();
      playlist = db.getData('/');
      playlist = playlist[args.playlist.join(' ')];

      if (!playlist || playlist.length === 0) {
        /**
        * Error condition is encountered.
        * @fires error
        */
        return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'notFound'), Bastion.strings.error(message.guild.language, 'notFound', true, 'song/playlist'), textChannel);
      }

      song = playlist.shift();

      message.channel.send({
        embed: {
          description: `Adding ${playlist.length + 1} favourite songs to the queue...`
        }
      }).catch(e => {
        Bastion.log.error(e);
      });

      // TODO: This executes before `args` is added to the queue, so the first song (`args`) is added later in the queue. Using setTimeout or flags is inefficient, find an efficient way to fix this!
      playlist.forEach(e => {
        e = /^(http[s]?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(e) ? e : `ytsearch:${e}`;
        yt.getInfo(e, [ '-q', '-i', '--skip-download', '--no-warnings', '--format=bestaudio[protocol^=http]' ], (err, info) => {
          if (err || info.format_id === undefined || info.format_id.startsWith('0')) return;
          message.guild.music.songs.push({
            url: info.formats[info.formats.length - 1].url,
            id: info.id,
            title: info.title,
            thumbnail: info.thumbnail,
            duration: info.duration,
            requester: message.author.tag
          });
        });
      });
    }
    else return;

    song = /^(http[s]?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/i.test(song) ? song : `ytsearch:${song}`;

    yt.getInfo(song, [ '-q', '-i', '--skip-download', '--no-warnings', '--format=bestaudio[protocol^=http]' ], (err, info) => {
      if (err || info.format_id === undefined || info.format_id.startsWith('0')) {
        return message.channel.send({
          embed: {
            color: Bastion.colors.RED,
            description: Bastion.strings.error(message.guild.language, 'notFound', true, 'result')
          }
        }).catch(e => {
          Bastion.log.error(e);
        });
      }

      message.guild.music.songs.push({
        url: info.formats[info.formats.length - 1].url,
        id: info.id,
        title: info.title,
        thumbnail: info.thumbnail,
        duration: info.duration,
        requester: message.author.tag
      });
      textChannel.send({
        embed: {
          color: Bastion.colors.GREEN,
          title: 'Added to the queue',
          url: info.id ? `https://youtu.be/${info.id}` : '',
          description: info.title,
          thumbnail: {
            url: info.thumbnail
          },
          footer: {
            text: `Position: ${message.guild.music.songs.length} | Duration: ${info.duration || 'N/A'} | Requester: ${message.author.tag}`
          }
        }
      }).catch(e => {
        Bastion.log.error(e);
      });
      if (message.guild.music && message.guild.music.playing) return;

      voiceChannel.join().then(connection => {
        message.guild.me.setDeaf(true).catch(() => {});

        startStreamDispatcher(message.guild, connection);
      }).catch(e => {
        Bastion.log.error(e);
      });
    });
  }
  catch (e) {
    Bastion.log.error(e);
    /**
    * Error condition is encountered.
    * @fires error
    */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'unknown'), Bastion.strings.error(message.guild.language, 'unknown', true), textChannel);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'song', type: String, multiple: true, defaultOption: true },
    { name: 'ytpl', type: String, alias: 'l' },
    { name: 'playlist', type: String, multiple: true, alias: 'p', defaultValue: [ 'default' ] }
  ]
};

exports.help = {
  name: 'play',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'play < name | song_link | -l <playlist_link> | -p [Playlist Name] >',
  example: [ 'play Shape of you', 'play -l https://www.youtube.com/playlist?list=PL4zQ6RXLMCJx4RD3pyzRX4QYFubtCdn_k', 'play -p My Favs', 'play -p' ]
};

/**
 * Starts a Stream Dispatcher in the specified guild
 * @function startStreamDispatcher
 * @param {Guild} guild the guild object where this command was ran
 * @param {VoiceConnection} connection the VoiceConnection of Bastion in this guild
 * @returns {void}
 */
function startStreamDispatcher(guild, connection) {
  if (guild.music.songs[0] === undefined) {
    return guild.music.textChannel.send({
      embed: {
        color: guild.client.colors.RED,
        description: 'Exiting voice channel.'
      }
    }).then(() => {
      guild.music.dispatcher.end();
      guild.music.voiceChannel.leave();
    }).catch(e => {
      guild.client.log.error(e);
    });
  }

  guild.music.dispatcher = connection.playStream(yt(guild.music.songs[0].url), { passes: 1, bitrate: 'auto' });
  guild.music.playing = true;

  guild.music.textChannel.send({
    embed: {
      color: guild.client.colors.BLUE,
      title: 'Playing',
      url: guild.music.songs[0].id ? `https://youtu.be/${guild.music.songs[0].id}` : '',
      description: guild.music.songs[0].title,
      thumbnail: {
        url: guild.music.songs[0].thumbnail
      },
      footer: {
        text: `🔉 ${guild.music.dispatcher.volume * 50}% | Duration: ${guild.music.songs[0].duration || 'N/A'} | Requester: ${guild.music.songs[0].requester}`
      }
    }
  }).catch(e => {
    guild.client.log.error(e);
  });

  guild.music.dispatcher.on('end', () => {
    guild.music.playing = false;
    guild.music.skipVotes = [];
    if (!guild.music.repeat) {
      guild.music.songs.shift();
    }
    else {
      guild.music.repeat = false;
    }
    startStreamDispatcher(guild, connection);
  });

  guild.music.dispatcher.on('error', (err) => {
    guild.music.playing = false;
    guild.music.songs.shift();
    startStreamDispatcher(guild, connection);
    return guild.client.log.error(err);
  });
}
<|endoftext|>


<commit_before>const
  spawn = require("./spawn"),
  quoteIfRequired = require("./quote-if-required"),
  splitPath = require("./split-path"),
  env = require("./env"),
  findLocalNuget = require("./find-local-nuget");

function isDotnetCore(binaryPath) {
  const
    trimmed = binaryPath.replace(/^"/, "")
              .replace(/"$/, ""),
    parts = splitPath(trimmed),
    executable = (parts[parts.length-1] || "");
  return !!executable.match(/^dotnet(:?\.exe)?$/i);
}

async function nugetPush(packageFile, sourceName) {
  const
    nuget = await findLocalNuget(),
    args = [
      "push",
      quoteIfRequired(packageFile),
      "-source",
      sourceName || "nuget.org"
    ];
  if (isDotnetCore(nuget)) {
    // ffs dotnet core breaks things that used to be simple
    // -> _some_ nuget commands require 'dotnet nuget ...'
    // -> _others_ don't, eg 'dotnet restore'
    args.splice(0, 0, "nuget");
  }
  if (env.resolveFlag("DRY_RUN")) {
    console.log(`nuget publish dry run: ${nuget} ${args.join(" ")}`);
    return;
  }
  console.log(`pushing package ${packageFile}`);
  return spawn(nuget, args);
}

module.exports = nugetPush;
<commit_msg>:rage4: why can't the nuget peeps keep a consistent interface?<commit_after>const
  spawn = require("./spawn"),
  quoteIfRequired = require("./quote-if-required"),
  splitPath = require("./split-path"),
  env = require("./env"),
  findLocalNuget = require("./find-local-nuget");

function isDotnetCore(binaryPath) {
  const
    trimmed = binaryPath.replace(/^"/, "")
              .replace(/"$/, ""),
    parts = splitPath(trimmed),
    executable = (parts[parts.length-1] || "");
  return !!executable.match(/^dotnet(:?\.exe)?$/i);
}

async function nugetPush(packageFile, sourceName) {
  const
    nuget = await findLocalNuget(),
    dnc = isDotnetCore(nuget),
    sourceArg = dnc ? "--source" : "-Source",
    // ffs dotnet core breaks things that used to be simple
    // -> _some_ nuget commands require 'dotnet nuget ...'
    // -> _others_ don't, eg 'dotnet restore'
    start = dnc ? [ "nuget" ] : [],
    args = start.concat([
      "push",
      quoteIfRequired(packageFile),
      sourceArg,
      sourceName || "nuget.org"
    ]);
  if (env.resolveFlag("DRY_RUN")) {
    console.log(`nuget publish dry run: ${nuget} ${args.join(" ")}`);
    return;
  }
  console.log(`pushing package ${packageFile}`);
  return spawn(nuget, args);
}

module.exports = nugetPush;
<|endoftext|>


<commit_before>/*
 *  Copyright 2014 TWO SIGMA INVESTMENTS, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * M_evaluatorManager
 * This module loads/unloads plugins and evaluators and maintains the lists. It also serves as the
 * single point of contact of all eval plugins.
 */
(function() {
  'use strict';
  angular.module('M_evaluatorManager', [
        'M_generalUtils',
        'M_bkSession',
        'M_bkUtils',
        'M_bkHelper', // This is only for ensuring that window.bkHelper is set, don't use bkHelper directly
        'M_bkCore', // TODO, we need to get rid of this dependency
        'M_menuPlugin' // TODO, we need to get rid of this dependency
      ])
      .factory('evaluatorManager', function(
          generalUtils,
          bkUtils,
          bkSession,
          bkBaseSessionModel, // TODO, we need to get rid of this dependency
          menuPluginManager // TODO, we need to get rid of this dependency
          ) {
        var nameToUrl = {// for known plugins, so we can refer to the plugin with either its name or URL
          "IPython": "./plugin/evaluator/ipython.js",
          "Julia": "./plugin/evaluator/julia.js",
          "R": "./plugin/evaluator/r.js",
          "Groovy": "./plugin/evaluator/groovy.js",
          "JavaScript": "./plugin/evaluator/javaScript.js",
          "Latex": "./plugin/evaluator/latex.js",
          "Html": "./plugin/evaluator/html.js"
        };
        var knownEvaluators = [
          {name: "IPython"},
          {name: "Julia"},
          {name: "R"},
          {name: "Groovy"},
          {name: "Html"},
          {name: "JavaScript"},
          {name: "Latex"}
        ];
        var plugins = [];
        var evaluators = [];
        var evaluatorsAndLoadingPlugins = [];
        var setEvaluators = function(map) {
          // XXX this should suffice but a copy of this pointer is escaping.
          // nameToUrl = {}
          for (var key in nameToUrl) {
            delete nameToUrl[key];
          }
          knownEvaluators = [];
          for (var key in map) {
            if (map.hasOwnProperty(key)) {
              nameToUrl[key] = map[key];
              knownEvaluators.push({name: key});
            }
          }
        }
        var getPlugin = function(pluginName, cb) {
          if (_.find(plugins, function(it) {
            return it.name === pluginName;
          })) {
            cb(_.find(plugins,function(it) {
              return it.name === pluginName;
            }).plugin);
          } else {
            setupPlugin(pluginName, false, cb);
          }
        };
        var updateEvaluatorsAndLoadingPlugins = function() {
          var i, j;
          evaluatorsAndLoadingPlugins = [];
          for (i in evaluators) {
            evaluators[i].loading = false;
            evaluatorsAndLoadingPlugins.push(evaluators[i]);
          }
          for (i in plugins) {
            var p = plugins[i];
            var found = false;
            for (j in evaluators) {
              var ep = evaluators[j].evaluator.settings.plugin;
              if (p.url == ep || p.url == nameToUrl[ep]) {
                found = true;
              }
            }
            if (found) continue;
            evaluatorsAndLoadingPlugins.push({loading: true, url: p.url});
          }
        };
        var updateEvaluatorsMenu = function(evaluator) {
          var actions = [];
          var name = evaluator.pluginName;
          for (var property in evaluator.spec) {
            var widg = evaluator.spec[property];
            var item = widg.name ? widg.name : widg.action;
            if (widg.type === "action") {
              actions.push({name: item, action: function() {
                evaluator.perform(widg.action);
              }});
            }
          }
          if (actions.length > 0)
            menuPluginManager.addItem("Evaluators", name, actions);
        };
        var newEvaluator = function(settings, alwaysCreateNewEvaluator) {
          getPlugin(settings.plugin, function(Shell) {
            if (!Shell) {
              console.error("you need to setup plugin first");
            }
            if (alwaysCreateNewEvaluator) { // don't reuse even if found shell matching the ID in settings
              settings.shellID = null;
            }
            var evaluator = new Shell(settings, function() {
              // The callback is for when the plugin has
              // finished initialization.  Note that it
              // cannot be called synchronously, since
              // then evaluator would not yet be
              // defined.
              evaluators.push({name: settings.name, evaluator: evaluator});
              updateEvaluatorsMenu(evaluator);
              updateEvaluatorsAndLoadingPlugins();
              bkUtils.refreshRootScope();
            });
          });
        };
        var setupPlugin = function(nameOrUrl, makeEvaluator, cb) {
          var url = nameToUrl[nameOrUrl] ? nameToUrl[nameOrUrl] : nameOrUrl;
          var pluginObj = _.find(plugins, function(it) {
            return it.url === url;
          });
          if (pluginObj) {
            if (cb) {
              if (pluginObj.status === "ready") {
                cb(pluginObj.plugin);
              } else {
                pluginObj.callbackOnReady.push(cb);
              }
            }
            return;
          } else {
            pluginObj = {
              url: url,
              name: "",
              plugin: null,
              status: "not ready",
              callbackOnReady: cb ? [cb] : [],
              onReady: function(MyShell) {
                this.status = "ready";
                this.name = MyShell.prototype.pluginName;
                this.plugin = MyShell;
                for (var i = 0; i < this.callbackOnReady.length; ++i) {
                  this.callbackOnReady[i](MyShell);
                }
                this.displayMessage = this.name + "(" + this.status + ")";
                if (makeEvaluator) {
                  var newEvaluatorObj = {
                    name: pluginObj.name,
                    plugin: pluginObj.name
                  };
                  bkBaseSessionModel.getNotebookModel().evaluators.push(newEvaluatorObj);
                  newEvaluator(newEvaluatorObj, true);
                }
                bkUtils.refreshRootScope();
              },
              displayMessage: "loading from " + url
            };
            bkUtils.loadingPlugins.add(url, pluginObj);
            plugins.push(pluginObj);
            bkSession.recordLoadedPlugin(pluginObj.name, pluginObj.url);
            updateEvaluatorsAndLoadingPlugins();
            generalUtils.loadJS(url, function() {
            }, function() {
              alert("could not load plugin, bad url:\n" + url);
            });
          }
        }
        return {
          setupPlugin: setupPlugin,
          getPlugins: function() {
            return plugins;
          },
          getPlugin: getPlugin,
          newEvaluator: newEvaluator,
          setEvaluators: setEvaluators,
          nameToUrl: nameToUrl,
          createEvaluatorThenExit: function(settings) {
            this.getPlugin(settings.plugin, function(Shell) {
              if (!Shell) {
                console.error("you need to setup plugin first");
              }
              var temp = {};
              temp.evaluator = new Shell(settings, function() {
                if (temp.evaluator.exit) {
                  temp.evaluator.exit();
                }
              });
            });
          },
          getEvaluator: function(evaluatorName) {
            return _.find(evaluators, function(it) {
              return it.name === evaluatorName;
            });
          },
          getAllEvaluators: function() {
            return evaluators;
          },
          getEvaluatorsAndLoadingPlugins: function() {
            return evaluatorsAndLoadingPlugins;
          },
          getKnownEvaluators: function() {
            return knownEvaluators;
          },
          removeAllEvaluators: function() {
            menuPluginManager.clearItem("Evaluators");
            evaluators.splice(0, evaluators.length);
          },
          reset: function() {
            evaluatorsAndLoadingPlugins.splice(0, evaluatorsAndLoadingPlugins.length);
            plugins.splice(0, plugins.length);
            this.removeAllEvaluators();
          },
          exitAllEvaluators: function() {
            _.each(evaluators, function(ev) {
              if (ev.evaluator && ev.evaluator.exit) {
                ev.evaluator.exit();
              }
            });
            this.removeAllEvaluators();
          },
          getViewModel: function() {
            var ret = {};
            _.each(evaluators, function(ev) {
              ret[ev.name] = {
                cm: {
                  "background": ev.evaluator.background,
                  "mode": ev.evaluator.cmMode
                }
              };
            });
            return ret;
          }
        };
      });
})();
<commit_msg>Fixed a bug when loading eval plugins that are not known in advance<commit_after>/*
 *  Copyright 2014 TWO SIGMA INVESTMENTS, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * M_evaluatorManager
 * This module loads/unloads plugins and evaluators and maintains the lists. It also serves as the
 * single point of contact of all eval plugins.
 */
(function() {
  'use strict';
  angular.module('M_evaluatorManager', [
        'M_generalUtils',
        'M_bkSession',
        'M_bkUtils',
        'M_bkHelper', // This is only for ensuring that window.bkHelper is set, don't use bkHelper directly
        'M_bkCore', // TODO, we need to get rid of this dependency
        'M_menuPlugin' // TODO, we need to get rid of this dependency
      ])
      .factory('evaluatorManager', function(
          generalUtils,
          bkUtils,
          bkSession,
          bkBaseSessionModel, // TODO, we need to get rid of this dependency
          menuPluginManager // TODO, we need to get rid of this dependency
          ) {
        var nameToUrl = {// for known plugins, so we can refer to the plugin with either its name or URL
          "IPython": "./plugin/evaluator/ipython.js",
          "Julia": "./plugin/evaluator/julia.js",
          "R": "./plugin/evaluator/r.js",
          "Groovy": "./plugin/evaluator/groovy.js",
          "JavaScript": "./plugin/evaluator/javaScript.js",
          "Latex": "./plugin/evaluator/latex.js",
          "Html": "./plugin/evaluator/html.js"
        };
        var knownEvaluators = [
          {name: "IPython"},
          {name: "Julia"},
          {name: "R"},
          {name: "Groovy"},
          {name: "Html"},
          {name: "JavaScript"},
          {name: "Latex"}
        ];
        var plugins = [];
        var evaluators = [];
        var evaluatorsAndLoadingPlugins = [];
        var setEvaluators = function(map) {
          // XXX this should suffice but a copy of this pointer is escaping.
          // nameToUrl = {}
          for (var key in nameToUrl) {
            delete nameToUrl[key];
          }
          knownEvaluators = [];
          for (var key in map) {
            if (map.hasOwnProperty(key)) {
              nameToUrl[key] = map[key];
              knownEvaluators.push({name: key});
            }
          }
        }
        var getPlugin = function(pluginName, cb) {
          if (_.find(plugins, function(it) {
            return it.name === pluginName;
          })) {
            cb(_.find(plugins,function(it) {
              return it.name === pluginName;
            }).plugin);
          } else {
            setupPlugin(pluginName, false, cb);
          }
        };
        var updateEvaluatorsAndLoadingPlugins = function() {
          var i, j;
          evaluatorsAndLoadingPlugins.splice(0, evaluatorsAndLoadingPlugins.length);
          for (i in evaluators) {
            evaluators[i].loading = false;
            evaluatorsAndLoadingPlugins.push(evaluators[i]);
          }
          for (i in plugins) {
            var p = plugins[i];
            var found = false;
            for (j in evaluators) {
              var ep = evaluators[j].evaluator.settings.plugin;
              if (p.url == ep || p.url == nameToUrl[ep]) {
                found = true;
              }
            }
            if (found) continue;
            evaluatorsAndLoadingPlugins.push({loading: true, url: p.url});
          }
        };
        var updateEvaluatorsMenu = function(evaluator) {
          var actions = [];
          var name = evaluator.pluginName;
          for (var property in evaluator.spec) {
            var widg = evaluator.spec[property];
            var item = widg.name ? widg.name : widg.action;
            if (widg.type === "action") {
              actions.push({name: item, action: function() {
                evaluator.perform(widg.action);
              }});
            }
          }
          if (actions.length > 0)
            menuPluginManager.addItem("Evaluators", name, actions);
        };
        var newEvaluator = function(settings, alwaysCreateNewEvaluator) {
          getPlugin(settings.plugin, function(Shell) {
            if (!Shell) {
              console.error("you need to setup plugin first");
            }
            if (alwaysCreateNewEvaluator) { // don't reuse even if found shell matching the ID in settings
              settings.shellID = null;
            }
            var evaluator = new Shell(settings, function() {
              // The callback is for when the plugin has
              // finished initialization.  Note that it
              // cannot be called synchronously, since
              // then evaluator would not yet be
              // defined.
              evaluators.push({name: settings.name, evaluator: evaluator});
              updateEvaluatorsMenu(evaluator);
              updateEvaluatorsAndLoadingPlugins();
              bkUtils.refreshRootScope();
            });
          });
        };
        var setupPlugin = function(nameOrUrl, makeEvaluator, cb) {
          var url = nameToUrl[nameOrUrl] ? nameToUrl[nameOrUrl] : nameOrUrl;
          var pluginObj = _.find(plugins, function(it) {
            return it.url === url;
          });
          if (pluginObj) {
            if (cb) {
              if (pluginObj.status === "ready") {
                cb(pluginObj.plugin);
              } else {
                pluginObj.callbackOnReady.push(cb);
              }
            }
            return;
          } else {
            pluginObj = {
              url: url,
              name: "",
              plugin: null,
              status: "not ready",
              callbackOnReady: cb ? [cb] : [],
              onReady: function(MyShell) {
                this.status = "ready";
                this.name = MyShell.prototype.pluginName;
                this.plugin = MyShell;
                for (var i = 0; i < this.callbackOnReady.length; ++i) {
                  this.callbackOnReady[i](MyShell);
                }
                this.displayMessage = this.name + "(" + this.status + ")";
                if (makeEvaluator) {
                  var newEvaluatorObj = {
                    name: pluginObj.name,
                    plugin: pluginObj.url
                  };
                  bkBaseSessionModel.getNotebookModel().evaluators.push(newEvaluatorObj);
                  newEvaluator(newEvaluatorObj, true);
                }
                bkUtils.refreshRootScope();
              },
              displayMessage: "loading from " + url
            };
            bkUtils.loadingPlugins.add(url, pluginObj);
            plugins.push(pluginObj);
            bkSession.recordLoadedPlugin(pluginObj.name, pluginObj.url);
            updateEvaluatorsAndLoadingPlugins();
            generalUtils.loadJS(url, function() {
            }, function() {
              alert("could not load plugin, bad url:\n" + url);
            });
          }
        }
        return {
          setupPlugin: setupPlugin,
          getPlugins: function() {
            return plugins;
          },
          getPlugin: getPlugin,
          newEvaluator: newEvaluator,
          setEvaluators: setEvaluators,
          nameToUrl: nameToUrl,
          createEvaluatorThenExit: function(settings) {
            this.getPlugin(settings.plugin, function(Shell) {
              if (!Shell) {
                console.error("you need to setup plugin first");
              }
              var temp = {};
              temp.evaluator = new Shell(settings, function() {
                if (temp.evaluator.exit) {
                  temp.evaluator.exit();
                }
              });
            });
          },
          getEvaluator: function(evaluatorName) {
            return _.find(evaluators, function(it) {
              return it.name === evaluatorName;
            });
          },
          getAllEvaluators: function() {
            return evaluators;
          },
          getEvaluatorsAndLoadingPlugins: function() {
            return evaluatorsAndLoadingPlugins;
          },
          getKnownEvaluators: function() {
            return knownEvaluators;
          },
          removeAllEvaluators: function() {
            menuPluginManager.clearItem("Evaluators");
            evaluators.splice(0, evaluators.length);
          },
          reset: function() {
            evaluatorsAndLoadingPlugins.splice(0, evaluatorsAndLoadingPlugins.length);
            plugins.splice(0, plugins.length);
            this.removeAllEvaluators();
          },
          exitAllEvaluators: function() {
            _.each(evaluators, function(ev) {
              if (ev.evaluator && ev.evaluator.exit) {
                ev.evaluator.exit();
              }
            });
            this.removeAllEvaluators();
          },
          getViewModel: function() {
            var ret = {};
            _.each(evaluators, function(ev) {
              ret[ev.name] = {
                cm: {
                  "background": ev.evaluator.background,
                  "mode": ev.evaluator.cmMode
                }
              };
            });
            return ret;
          }
        };
      });
})();
<|endoftext|>


<commit_before>window.Schedule = function (scheduleContainer, options) {
  var self = this;

  var NUM_COLORS        = 7;
  var TEXT_COLORS       = ['#d1265d', '#1577aa', '#bf8a2e', '#008a2e', '#853d80', '#9d5733', '#d9652b'];
  var BACKGROUND_COLORS = ['#ffd4df', '#ceeffc', '#fff4d0', '#dcf7da', '#f7e2f7', '#ede6df', '#ffe9cf'];
  var BORDER_COLORS     = ['#ff2066', '#00aff2', '#ffcb45', '#48da58', '#d373da', '#a48363', '#ff9332'];
  var SELECTED_COLORS   = ['#ff3575', '#19b5f2', '#ffcf56', '#59dc68', '#d57fdd', '#ac8f71', '#ff9c46'];
  // /*                         PINK       BLUE       YELLOW     GREEN      PURPLE     BROWN      ORANGE */

  // var NUM_COLORS        = 6;
  // var TEXT_COLORS       = ['#720', '#722', '#661', '#227', '#166', '#616'];
  // var BACKGROUND_COLORS = ['#fdc', '#edd', '#eed', '#dde', '#dee', '#ede'];
  // var BORDER_COLORS     = ['#b62', '#b44', '#994', '#448', '#499', '#949'];

  options = options || {};
  options.daySpan   = options.daySpan   || 5;
  options.dayBegin  = options.dayBegin  || 1;
  options.gridSize  = options.gridSize  || 60;
  options.timeSpan  = Math.ceil((options.timeSpan  || 720) / 60) * 60;
  options.timeBegin = Math.ceil((options.timeBegin || 480) / 60) * 60;

  var scheduleElement = document.createElement('schedule-view');
  var legendElement   = document.createElement('schedule-legend');
  var gridElement     = document.createElement('schedule-grid');

  scheduleElement.appendChild(legendElement);
  scheduleElement.appendChild(gridElement);
  scheduleContainer.appendChild(scheduleElement);

  var daySize = function (n) {
    return n * (100 / options.daySpan) + '%';
  };

  var timeSize = function (n) {
    return n * (100 / options.timeSpan) + '%';
  };

  self.addEvent = function (event) {
    var eventText       = document.createElement('event-text');
    var eventElement    = document.createElement('schedule-event');
    var eventBackground = document.createElement('event-background');
    var colorIndex      = event.colorNum % NUM_COLORS;

    eventText.innerHTML                   = event.title.join('
');
    eventText.style.color                 = TEXT_COLORS[colorIndex];
    eventElement.style.top                = timeSize(event.start - options.timeBegin);
    eventElement.style.left               = daySize(event.day - options.dayBegin);
    eventElement.style.width              = 'calc(' + daySize(1) + ' - 6px)';
    eventElement.style.height             = 'calc(' + timeSize(event.end - event.start) + ' - 2px)';
    eventElement.style.borderColor        = BORDER_COLORS[colorIndex];
    eventBackground.style.backgroundColor = BACKGROUND_COLORS[colorIndex];

    eventElement.appendChild(eventBackground);
    eventElement.appendChild(eventText);
    scheduleElement.appendChild(eventElement);
  };

  self.clearEvents = function () {
    var events = scheduleElement.querySelectorAll('schedule-event');
    for (var e = 0; e < events.length; ++e)
      events[e].parentNode.removeChild(events[e]);
  };

  self.setEvents = function (events) {
    self.clearEvents();
    events.forEach(function (event) {
      self.addEvent(event);
    });
  };

  self.destroy = function () {
    scheduleContainer.removeChild(scheduleElement);
  };

  var drawLegend = function () {
    for (var r = 1; r < options.timeSpan / 60; ++r) {
      var hourElement = document.createElement('legend-hour');
      var hour = options.timeBegin / 60 + r;

      if      (hour == 0)  hour = '12 AM';
      else if (hour == 12) hour = 'Noon';
      else if (hour < 12)  hour = hour + ' AM';
      else                 hour = hour - 12 + ' PM';

      hourElement.textContent = hour;
      hourElement.style.top = 'calc(' + timeSize(60 * r) + ' - 0.75em)';
      legendElement.appendChild(hourElement);
    }
  };

  var drawGrid = function () {
    for (var d = 0; d < options.daySpan; ++d) {
      var dayElement = document.createElement('grid-day');
      dayElement.style.width = daySize(1);

      for (var r = 0; r < options.timeSpan / options.gridSize; ++r) {
        var hourElement = document.createElement('grid-hour');
        hourElement.style.height = timeSize(options.gridSize);
        dayElement.appendChild(hourElement);
      }

      gridElement.appendChild(dayElement);
    }
  };

  drawLegend();
  drawGrid();
};
<commit_msg>Added optional tooltip to events in the schedule grid<commit_after>window.Schedule = function (scheduleContainer, options) {
  var self = this;

  var NUM_COLORS        = 7;
  var TEXT_COLORS       = ['#d1265d', '#1577aa', '#bf8a2e', '#008a2e', '#853d80', '#9d5733', '#d9652b'];
  var BACKGROUND_COLORS = ['#ffd4df', '#ceeffc', '#fff4d0', '#dcf7da', '#f7e2f7', '#ede6df', '#ffe9cf'];
  var BORDER_COLORS     = ['#ff2066', '#00aff2', '#ffcb45', '#48da58', '#d373da', '#a48363', '#ff9332'];
  var SELECTED_COLORS   = ['#ff3575', '#19b5f2', '#ffcf56', '#59dc68', '#d57fdd', '#ac8f71', '#ff9c46'];
  // /*                         PINK       BLUE       YELLOW     GREEN      PURPLE     BROWN      ORANGE */

  // var NUM_COLORS        = 6;
  // var TEXT_COLORS       = ['#720', '#722', '#661', '#227', '#166', '#616'];
  // var BACKGROUND_COLORS = ['#fdc', '#edd', '#eed', '#dde', '#dee', '#ede'];
  // var BORDER_COLORS     = ['#b62', '#b44', '#994', '#448', '#499', '#949'];

  options = options || {};
  options.daySpan   = options.daySpan   || 5;
  options.dayBegin  = options.dayBegin  || 1;
  options.gridSize  = options.gridSize  || 60;
  options.timeSpan  = Math.ceil((options.timeSpan  || 720) / 60) * 60;
  options.timeBegin = Math.ceil((options.timeBegin || 480) / 60) * 60;

  var scheduleElement = document.createElement('schedule-view');
  var legendElement   = document.createElement('schedule-legend');
  var gridElement     = document.createElement('schedule-grid');

  scheduleElement.appendChild(legendElement);
  scheduleElement.appendChild(gridElement);
  scheduleContainer.appendChild(scheduleElement);

  var daySize = function (n) {
    return n * (100 / options.daySpan) + '%';
  };

  var timeSize = function (n) {
    return n * (100 / options.timeSpan) + '%';
  };

  self.addEvent = function (event) {
    var eventText       = document.createElement('event-text');
    var eventElement    = document.createElement('schedule-event');
    var eventBackground = document.createElement('event-background');
    var colorIndex      = event.colorNum % NUM_COLORS;

    eventText.innerHTML                   = event.title.join('
');
    eventText.style.color                 = TEXT_COLORS[colorIndex];
    eventElement.style.top                = timeSize(event.start - options.timeBegin);
    eventElement.style.left               = daySize(event.day - options.dayBegin);
    eventElement.style.width              = 'calc(' + daySize(1) + ' - 6px)';
    eventElement.style.height             = 'calc(' + timeSize(event.end - event.start) + ' - 2px)';
    eventElement.style.borderColor        = BORDER_COLORS[colorIndex];
    eventBackground.style.backgroundColor = BACKGROUND_COLORS[colorIndex];

    if("tooltip" in event) {
      eventElement.title = event.tooltip;
    }

    eventElement.appendChild(eventBackground);
    eventElement.appendChild(eventText);
    scheduleElement.appendChild(eventElement);
  };

  self.clearEvents = function () {
    var events = scheduleElement.querySelectorAll('schedule-event');
    for (var e = 0; e < events.length; ++e)
      events[e].parentNode.removeChild(events[e]);
  };

  self.setEvents = function (events) {
    self.clearEvents();
    events.forEach(function (event) {
      self.addEvent(event);
    });
  };

  self.destroy = function () {
    scheduleContainer.removeChild(scheduleElement);
  };

  var drawLegend = function () {
    for (var r = 1; r < options.timeSpan / 60; ++r) {
      var hourElement = document.createElement('legend-hour');
      var hour = options.timeBegin / 60 + r;

      if      (hour == 0)  hour = '12 AM';
      else if (hour == 12) hour = 'Noon';
      else if (hour < 12)  hour = hour + ' AM';
      else                 hour = hour - 12 + ' PM';

      hourElement.textContent = hour;
      hourElement.style.top = 'calc(' + timeSize(60 * r) + ' - 0.75em)';
      legendElement.appendChild(hourElement);
    }
  };

  var drawGrid = function () {
    for (var d = 0; d < options.daySpan; ++d) {
      var dayElement = document.createElement('grid-day');
      dayElement.style.width = daySize(1);

      for (var r = 0; r < options.timeSpan / options.gridSize; ++r) {
        var hourElement = document.createElement('grid-hour');
        hourElement.style.height = timeSize(options.gridSize);
        dayElement.appendChild(hourElement);
      }

      gridElement.appendChild(dayElement);
    }
  };

  drawLegend();
  drawGrid();
};
<|endoftext|>


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

module.exports = function confgureServer(config, app) {
  var app = app || express();

  if (_.get(config, 'server.options.logs')) {
    app.use(require('morgan')('dev'));
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use('\.html$', express.static(config.destPath));

  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge'),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('/', function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest
    });
  });

  return app;
};
<commit_msg>Add default STATIC_CACHE_AGE to server.site.js<commit_after>var _ = require('lodash'),
    express = require('express'),
    STATIC_CACHE_AGE = 365 * 24 * 60 * 60 * 1000; // 1 year;

module.exports = function confgureServer(config, app) {
  var app = app || express();

  if (_.get(config, 'server.options.logs')) {
    app.use(require('morgan')('dev'));
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use('\.html$', express.static(config.destPath));

  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge', STATIC_CACHE_AGE),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('/', function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest
    });
  });

  return app;
};
<|endoftext|>


<commit_before>describe("About Mutability", function() {

  it("should expect object properties to be public and mutable", function () {
    var aPerson = {firstname: "John", lastname: "Smith" };
    aPerson.firstname = "Alan";

    expect(aPerson.firstname).toBe(FILL_ME_IN);
  });

  it("should understand that constructed properties are public and mutable", function () {
    function Person(firstname, lastname)
    {
      this.firstname = firstname;
      this.lastname = lastname;
    }
    var aPerson = new Person ("John", "Smith");
    aPerson.firstname = "Alan";

    expect(aPerson.firstname).toBe(FILL_ME_IN);
  });

  it("should expect prototype properties to be public and mutable", function () {
    function Person(firstname, lastname)
    {
      this.firstname = firstname;
      this.lastname = lastname;
    }
    Person.prototype.getFullName = function () {
      return this.firstname + " " + this.lastname;
    };

    var aPerson = new Person ("John", "Smith");
    expect(aPerson.getFullName()).toBe(FILL_ME_IN);

    aPerson.getFullName = function () {
      return this.lastname + ", " + this.firstname;
    };

    expect(aPerson.getFullName()).toBe(FILL_ME_IN);
  });

  it("should know that variables inside a constructor and constructor args are private", function () {
    function Person(firstname, lastname)
    {
      var fullName = firstname + " " + lastname;

      this.getFirstName = function () { return firstname; };
      this.getLastName = function () { return lastname; };
      this.getFullName = function () { return fullName; };
    }
    var aPerson = new Person ("John", "Smith");

    aPerson.firstname = "Penny";
    aPerson.lastname = "Andrews";
    aPerson.fullName = "Penny Andrews";

    expect(aPerson.getFirstName()).toBe(FILL_ME_IN);
    expect(aPerson.getLastName()).toBe(FILL_ME_IN);
    expect(aPerson.getFullName()).toBe(FILL_ME_IN);

    aPerson.getFullName = function () {
      return aPerson.lastname + ", " + aPerson.firstname;
    };

    expect(aPerson.getFullName()).toBe(FILL_ME_IN);
  });

});
<commit_msg>finished mutability<commit_after>describe("About Mutability", function() {

  it("should expect object properties to be public and mutable", function () {
    var aPerson = {firstname: "John", lastname: "Smith" };
    aPerson.firstname = "Alan";

    expect(aPerson.firstname).toBe("Alan");
  });

  it("should understand that constructed properties are public and mutable", function () {
    function Person(firstname, lastname)
    {
      this.firstname = firstname;
      this.lastname = lastname;
    }
    var aPerson = new Person ("Alan", "Smith");
    aPerson.firstname = "Alan";

    expect(aPerson.firstname).toBe("Alan");
  });

  it("should expect prototype properties to be public and mutable", function () {
    function Person(firstname, lastname)
    {
      this.firstname = firstname;
      this.lastname = lastname;
    }
    Person.prototype.getFullName = function () {
      return this.firstname + " " + this.lastname;
    };

    var aPerson = new Person ("John", "Smith");
    expect(aPerson.getFullName()).toBe("John Smith");

    aPerson.getFullName = function () {
      return this.lastname + ", " + this.firstname;
    };

    expect(aPerson.getFullName()).toBe("Smith, John");
  });

  it("should know that variables inside a constructor and constructor args are private", function () {
    function Person(firstname, lastname)
    {
      var fullName = firstname + " " + lastname;

      this.getFirstName = function () { return firstname; };
      this.getLastName = function () { return lastname; };
      this.getFullName = function () { return fullName; };
    }
    var aPerson = new Person ("John", "Smith");

    aPerson.firstname = "Penny";
    aPerson.lastname = "Andrews";
    aPerson.fullName = "Penny Andrews";

    expect(aPerson.getFirstName()).toBe("John");
    expect(aPerson.getLastName()).toBe("Smith");
    expect(aPerson.getFullName()).toBe("John Smith");

    aPerson.getFullName = function () {
      return aPerson.lastname + ", " + aPerson.firstname;
    };

    expect(aPerson.getFullName()).toBe("Andrews, Penny");
  });

});
<|endoftext|>


<commit_before>/**
 * @package assets
 */

(function($) {

	/**
	 * This plugin makes items collapsible.
	 *
	 * @name $.symphonyCollapsible
	 * @class
	 *
	 * @param {Object} options An object specifying containing the attributes specified below
	 * @param {String} [options.items='.instance'] Selector to find collapsible items within the container
	 * @param {String} [options.handles='.header:first'] Selector to find clickable handles to trigger interaction
	 * @param {String} [options.content='.content'] Selector to find hideable content area
	 * @param {String} [options.controls=true] Add controls to collapse/expand all instances
	 * @param {String} [options.save_state=true] Stores states of instances using local storage
	 * @param {String} [options.storage='symphony.collapsible.id.env'] Namespace used for local storage
	 *
	 *	@example

			var collapsible = $('#duplicator').symphonyCollapsible({
				items:		'.instance',
				handles:	'.header span'
			});
			collapsible.collapseAll();
	 */
	$.fn.symphonyCollapsible = function(options) {
		var objects = this,
			settings = {
				items:				'.instance',
				handles:			'.header:first',
				content:			'.content',
				controls:			true,
				save_state:			true,
				storage: 			'symphony.collapsible.' + $('body').attr('id') + (Symphony.Context.get('env')[1] ? '.' + Symphony.Context.get('env')[1] : '')
			};

		$.extend(settings, options);

	/*-----------------------------------------------------------------------*/

		// Language strings
		Symphony.Language.add({
			'Expand all': false,
			'Collapse all': false
		});

	/*-----------------------------------------------------------------------*/

		objects.each(function(index) {
			var object = $(this),
				storage = settings.storage + '.' + index + '.collapsed';

		/*-------------------------------------------------------------------*/

			// Collapse item
			object.on('collapse.collapsible', settings.items, function(event, imediate) {
				var item = $(this),
					content = item.find(settings.content),
					speed = 'fast';

				// Imediate switch?
				if(imediate === true) {
					speed = 0;
				}

				// Collapse item
				item.trigger('collapsestart.collapsible');
				content.slideUp(speed, function() {
					item.addClass('collapsed');
					item.trigger('collapsestop.collapsible');
				});
			});

			// Expand item
			object.on('expand.collapsible', settings.items, function(event) {
				var item = $(this),
					content = item.find(settings.content);

				// Collapse item
				item.trigger('expandstart.collapsible');
				content.slideDown('fast', function() {
					item.removeClass('collapsed');
					item.trigger('expandstop.collapsible');
				});
			});

			// Toggle single item
			object.on('click.collapsible', settings.handles, function() {
				var handle = $(this),
					item = handle.parents(settings.items);

				// Expand
				if(item.is('.collapsed')) {
					item.trigger('expand.collapsible');
				}

				// Collapse
				else {
					item.trigger('collapse.collapsible');
				}
			});

			// Toggle all
			object.on('click.collapsible', 'a.collapser', function(event) {
				var collapser = $(this),
					items = object.find(settings.items);

				// Collapse all
				if(collapser.text() == Symphony.Language.get('Collapse all')) {
					items.trigger('collapse.collapsible');
				}

				// Expand all
				else {
					items.trigger('expand.collapsible');
				}
			});

			// Update controls to expand all
			object.on('collapsestop.collapsible', settings.items, function(event) {
				var item = $(this);

				if(item.siblings().filter(':not(.collapsed)').size() == 0) {
					object.find('a.collapser').text(Symphony.Language.get('Expand all'));
				}
			});

			// Update controls to collapse all
			object.on('expandstop.collapsible', settings.items, function(event) {
				var item = $(this);

				if(item.siblings().filter('.collapsed').size() == 0) {
					object.find('a.collapser').text(Symphony.Language.get('Collapse all'));
				}
			});

			// Save states
			object.on('collapsestop.collapsible expandstop.collapsible', settings.items, function(event) {
				if(settings.save_state === true && Symphony.Support.localStorage === true) {
					var collapsed = object.find(settings.items).map(function(index) {
						if($(this).is('.collapsed')) {
							return index;
						};
					});
					localStorage[storage] = collapsed.get().join(',');
				}
			});

			// Restore states
			object.on('restore.collapsible', function(event) {
				if(settings.save_state === true && Symphony.Support.localStorage === true && localStorage[storage]) {
					$.each(localStorage[storage].split(','), function(index, value) {
						object.find(settings.items).eq(value).trigger('collapse.collapsible', [true]);
					});
				}
			});

		/*-------------------------------------------------------------------*/

			// Prepare interface
			object.addClass('collapsible');

			// Build controls
			if(settings.controls === true) {
				var top = object.find('> :first-child'),
					bottom = object.find('> :last-child'),
					collapser = $('<a />', {
						class: 'collapser',
						text: Symphony.Language.get('Collapse all')
					}),
					controls = $('<div />', {
						class: 'controls'
					}).append(collapser.clone());

				// Existing top controls
				if(top.is('.controls')) {
					top.prepend(collapser);
				}

				// Create missing top controls
				else {
					object.prepend(controls.addClass('top'));
				}

				// Existing bottom controls
				if(bottom.is('.controls')) {
					bottom.prepend(collapser);
				}

				// Create missing bottom controls
				else {
					object.append(controls);
				}
			}

			// Restore states
			object.trigger('restore.collapsible');
		});

	/*-----------------------------------------------------------------------*/

		return objects;
	};

})(jQuery.noConflict());
<commit_msg>Rename internal variable<commit_after>/**
 * @package assets
 */

(function($) {

	/**
	 * This plugin makes items collapsible.
	 *
	 * @name $.symphonyCollapsible
	 * @class
	 *
	 * @param {Object} options An object specifying containing the attributes specified below
	 * @param {String} [options.items='.instance'] Selector to find collapsible items within the container
	 * @param {String} [options.handles='.header:first'] Selector to find clickable handles to trigger interaction
	 * @param {String} [options.content='.content'] Selector to find hideable content area
	 * @param {String} [options.controls=true] Add controls to collapse/expand all instances
	 * @param {String} [options.save_state=true] Stores states of instances using local storage
	 * @param {String} [options.storage='symphony.collapsible.id.env'] Namespace used for local storage
	 *
	 *	@example

			var collapsible = $('#duplicator').symphonyCollapsible({
				items:		'.instance',
				handles:	'.header span'
			});
			collapsible.collapseAll();
	 */
	$.fn.symphonyCollapsible = function(options) {
		var objects = this,
			settings = {
				items:				'.instance',
				handles:			'.header:first',
				content:			'.content',
				controls:			true,
				save_state:			true,
				storage: 			'symphony.collapsible.' + $('body').attr('id') + (Symphony.Context.get('env')[1] ? '.' + Symphony.Context.get('env')[1] : '')
			};

		$.extend(settings, options);

	/*-----------------------------------------------------------------------*/

		// Language strings
		Symphony.Language.add({
			'Expand all': false,
			'Collapse all': false
		});

	/*-----------------------------------------------------------------------*/

		objects.each(function(index) {
			var object = $(this),
				storage = settings.storage + '.' + index + '.collapsed';

		/*-------------------------------------------------------------------*/

			// Collapse item
			object.on('collapse.collapsible', settings.items, function(event, instantly) {
				var item = $(this),
					content = item.find(settings.content),
					speed = 'fast';

				// Instant switch?
				if(instantly === true) {
					speed = 0;
				}

				// Collapse item
				item.trigger('collapsestart.collapsible');
				content.slideUp(speed, function() {
					item.addClass('collapsed');
					item.trigger('collapsestop.collapsible');
				});
			});

			// Expand item
			object.on('expand.collapsible', settings.items, function(event) {
				var item = $(this),
					content = item.find(settings.content);

				// Collapse item
				item.trigger('expandstart.collapsible');
				content.slideDown('fast', function() {
					item.removeClass('collapsed');
					item.trigger('expandstop.collapsible');
				});
			});

			// Toggle single item
			object.on('click.collapsible', settings.handles, function() {
				var handle = $(this),
					item = handle.parents(settings.items);

				// Expand
				if(item.is('.collapsed')) {
					item.trigger('expand.collapsible');
				}

				// Collapse
				else {
					item.trigger('collapse.collapsible');
				}
			});

			// Toggle all
			object.on('click.collapsible', 'a.collapser', function(event) {
				var collapser = $(this),
					items = object.find(settings.items);

				// Collapse all
				if(collapser.text() == Symphony.Language.get('Collapse all')) {
					items.trigger('collapse.collapsible');
				}

				// Expand all
				else {
					items.trigger('expand.collapsible');
				}
			});

			// Update controls to expand all
			object.on('collapsestop.collapsible', settings.items, function(event) {
				var item = $(this);

				if(item.siblings().filter(':not(.collapsed)').size() == 0) {
					object.find('a.collapser').text(Symphony.Language.get('Expand all'));
				}
			});

			// Update controls to collapse all
			object.on('expandstop.collapsible', settings.items, function(event) {
				var item = $(this);

				if(item.siblings().filter('.collapsed').size() == 0) {
					object.find('a.collapser').text(Symphony.Language.get('Collapse all'));
				}
			});

			// Save states
			object.on('collapsestop.collapsible expandstop.collapsible', settings.items, function(event) {
				if(settings.save_state === true && Symphony.Support.localStorage === true) {
					var collapsed = object.find(settings.items).map(function(index) {
						if($(this).is('.collapsed')) {
							return index;
						};
					});
					localStorage[storage] = collapsed.get().join(',');
				}
			});

			// Restore states
			object.on('restore.collapsible', function(event) {
				if(settings.save_state === true && Symphony.Support.localStorage === true && localStorage[storage]) {
					$.each(localStorage[storage].split(','), function(index, value) {
						object.find(settings.items).eq(value).trigger('collapse.collapsible', [true]);
					});
				}
			});

		/*-------------------------------------------------------------------*/

			// Prepare interface
			object.addClass('collapsible');

			// Build controls
			if(settings.controls === true) {
				var top = object.find('> :first-child'),
					bottom = object.find('> :last-child'),
					collapser = $('<a />', {
						class: 'collapser',
						text: Symphony.Language.get('Collapse all')
					}),
					controls = $('<div />', {
						class: 'controls'
					}).append(collapser.clone());

				// Existing top controls
				if(top.is('.controls')) {
					top.prepend(collapser);
				}

				// Create missing top controls
				else {
					object.prepend(controls.addClass('top'));
				}

				// Existing bottom controls
				if(bottom.is('.controls')) {
					bottom.prepend(collapser);
				}

				// Create missing bottom controls
				else {
					object.append(controls);
				}
			}

			// Restore states
			object.trigger('restore.collapsible');
		});

	/*-----------------------------------------------------------------------*/

		return objects;
	};

})(jQuery.noConflict());
<|endoftext|>