<commit_before>var godrawingboard = (function() {
  var ASYNC_URL = 'https://cdnjs.cloudflare.com/ajax/libs/async/0.2.7/async.min.js';
  var GO_DRAWINGBOARD_APP = 'https://goinstant.net/GoDrawingBoard.JS/GoDrawingBoard.JS';

  var SCRIPT_URLS = [
    ['https://cdn.goinstant.net/v1/platform.min.js', 'goinstant'],  //PLATFORM
    ['https://cdn.goinstant.net/widgets/user-list/latest/user-list.min.js', 'goinstant.widgets.UserList'],  // USER_LIST
    ['https://cdn.goinstant.net/widgets/click-indicator/latest/click-indicator.min.js', 'goinstant.widgets.ClickIndicator'],  // CLICK_INDICATOR
    ['https://cdn.goinstant.net/widgets/user-colors/latest/user-colors.min.js', 'goinstant.widgets.UserColors'],  // USER_COLORS
    ['https://cdn.goinstant.net/widgets/notifications/latest/notifications.min.js', 'goinstant.widgets.Notifications']  // NOTIFICATIONS
  ];

  var CSS_URLS = [
    'https://cdn.goinstant.net/widgets/user-list/latest/user-list.css',
    'https://cdn.goinstant.net/widgets/click-indicator/latest/click-indicator.css',
    'https://cdn.goinstant.net/widgets/notifications/latest/notifications.css'
  ];

  var ORIGIN_GO_DRAWINGBOARD = 'godrawingboard';
  var GO_DRAWINGBOARD_ID = 'go_reveal_room';
  var GO_DRAWINGBOARD_USER_NAME = 'go_drawingboard_user_name';
  var QUERY_REGEX = new RegExp('\\?(.*)\\b' + GO_DRAWINGBOARD_ID + '=([^&#\/]*)(.*)');

  var roomName;
  var userName;
  var alreadyLoaded;
  var drawingboardRoom;
  var slide;
  var query;

  function connectToPlatform(cb) {
    var platform = new goinstant.Platform(GO_DRAWINGBOARD_APP);
    var notifications;

    async.series([
      // connect to GoInstant platform
      platform.connect.bind(platform),

      // create (if needed) the room instance for the drawingboard and
      // join the room and gain access to the drawingboard stat information
      function(next) {
        drawingboardRoom = platform.room(roomName);
        console.log('Joined room: ', roomName);
        drawingboardRoom.join(next);
      },

      // subscribe to any notifications in the presentaiton room.
      function(next) {
        notifications = new goinstant.widgets.Notifications();
        notifications.subscribe(drawingboardRoom, next);
      },

      // set up the user's display name
      function(next) {
        if (alreadyLoaded) {
          return next();
        }

        var publishOpts = {
          room: drawingboardRoom,
          type: 'success',
          message: userName + ' has joined.'
        };

        drawingboardRoom.user(function(err, user, userKey) {
          if (err) {
            return next(err);
          }

          var displayNameKey = userKey.key('displayName');
          displayNameKey.set(userName, function(err) {
            if (err) {
              return next(err);
            }

            // publish a notification of the new user
            notifications.publish(publishOpts, next);
          });
        });
      },

      // select a colour for the current user
      function(next) {
        var opts = {
          room: drawingboardRoom
        };

        var userColors = new goinstant.widgets.UserColors(opts);
        userColors.choose(next);
      },

      // initialize the user list
      function(next) {
        var opts = {
          room: drawingboardRoom,
          position: 'right'
        };

        var userList = new goinstant.widgets.UserList(opts);
        userList.initialize(next);
      },

      // initialize the clicking indicator
      function(next) {
        var opts = {
          room: drawingboardRoom
        };

        var clickIndicator = new goinstant.widgets.ClickIndicator(opts);
        clickIndicator.initialize(next);
      },

      function(next) {
        drawingboardRoom.user(function(err, userObj, userKeyObj) {
          if (err) {
            throw err;
          }
          var defaultBoard = new DrawingBoard.Board('default-board', {
            goinstant: {
              room: drawingboardRoom,
              userKey: userKeyObj
            }
          });
        });
        next();
      },

      // initialize the sharing of the state of the drawingboard.
      initializeSharing
    ], cb);
  }

  function initializeSharing() {

    // We are interested in knowing if there is a new query on the URL when the
    // slide show is loaded. This detects the use of the query parameter in the
    // default slide deck to change the transitions and themes.
    var parser = document.createElement('a');
    parser.href = window.location.toString();

    // Create the sharing URL by adding the roomName as a query parameter to
    // the current window.location.
    if (parser.search) {
      parser.search += '&' + GO_DRAWINGBOARD_ID + '=' + roomName;
    } else {
      parser.search = '?' + GO_DRAWINGBOARD_ID + '=' + roomName;
    }

    // Create Share Button
    addShareButton(parser.href);
  }

  function addShareButton(text) {
    var shareBtn = document.createElement('div');
    var cssBtn = 'display: block; position: fixed; bottom: 1em; left: 0; ' +
      'z-index: 9999; height: 17px; padding: 9px; font-size: 15px; ' +
      'font-family: sans-serif; font-weight: bold; background: white; ' +
      'border-radius: 0 3px 3px 0; border: 1px solid #ccc; ' +
      'text-decoration: none; color: #15A815;';
    var cssURL = 'font-weight: regular;';

    shareBtn.innerHTML = 'Share';
    shareBtn.style.cssText = cssBtn;

    var board = document.getElementsByClassName('board')[0];
    board.parentNode.insertBefore(shareBtn, board);

    shareBtn.onmouseover = function() {
      if (this.poppedOut) {
        return;
      }
      this.poppedOut = true;

      this.innerHTML +=
        '<input id="gi-share-text" type="text" value="' + text +
        '" style="margin: -5px 0 0 15px; padding: 5px; width: 180px;"/>';

      this.style.width = '250px';
      document.getElementById('gi-share-text').select();
    };

    shareBtn.onmouseout = function(evt) {
      if (evt.relatedTarget && evt.relatedTarget.id === 'gi-share-text') {
        return;
      }
      this.poppedOut = false;

      this.innerHTML = 'Share';
      this.style.width = 'auto';
    };
  }

  function setRoomName() {
    // if we have the go-drawingboard room in sessionStorage then just connect to
    // the room and continue with the initialization.
    roomName = sessionStorage.getItem(GO_DRAWINGBOARD_ID);
    if (roomName) {
      return true;
    }

    // if we do not have the name in storage then check to see if the window
    // location contains a query string containing the id of the room.

    // creating an anchor tag and assigning the href to the window location
    // will automatically parse out the URL components ... sweet.
    var parser = document.createElement('a');
    parser.href = window.location.toString();

    var hasRoom = QUERY_REGEX.exec(parser.search);
    var roomId = hasRoom && hasRoom[2];
    if (roomId) {
      roomName = roomId.toString();
      // add the cookie to the document.
      sessionStorage.setItem(GO_DRAWINGBOARD_ID, roomName);

      // regenerate the URI without the go-drawingboard query parameter and reload
      // the page with the new URI.
      var beforeRoom = hasRoom[1];
      if (beforeRoom[beforeRoom.length - 1] === '&') {
        beforeRoom = beforeRoom.slice(0, beforeRoom.lengh - 1);
      }
      var searchStr = beforeRoom + hasRoom[3];
      if (searchStr.length > 0) {
        searchStr = '?' + searchStr;
      }

      parser.search = searchStr;

      // set the new location and discontinue the initialization.
      window.location = parser.href;
      return false;
    }

    // there is no room to join for this drawingboard so simply create a new
    // room and set the cookie in case of future refreshes.
    var id = Math.floor(Math.random() * Math.pow(2, 32));
    roomName = id.toString();
    sessionStorage.setItem(GO_DRAWINGBOARD_ID, roomName);

    return true;
  }

  function loadResources(cb) {
    function _loadResource(type, url, testProperty, loaded) {
      // if the object exists then just return, no need to reload.
      if (window[testProperty]) {
        return loaded();
      }

      // Adding the script tag to the head as suggested before
      var res = document.createElement(type);
      if (type === 'script') {
        res.src = url;
        res.type = 'text/javascript';
      } else {
        res.rel = 'stylesheet';
        res.type = 'text/css';
        res.href = url;
      }

      // callbacks for loaded notification
      function _handleLoadEvent() {
        return loaded();
      }
      res.onreadystatechange = _handleLoadEvent;
      res.onload = _handleLoadEvent;

      // Fire the loading
      var head = document.getElementsByTagName('head')[0];
      head.appendChild(res);
    }

    // get async if it does not already exist.
    _loadResource('script', ASYNC_URL, 'async', function() {

      // Load all the resources in the array. Create the array of resoures
      // to load and then load each.
      var loadRequests = [];
      async.series([
        // add function calls to load all scripts to the load requests array
        function(next) {
          async.each(SCRIPT_URLS, function(params, cont) {
            loadRequests.push(_loadResource.bind(null, 'script', params[0], params[1]));
            return cont();
          }, next);
        },

        // add function calls to load all css to the load requests array
        function(next) {
          async.each(CSS_URLS, function(url, cont) {
            loadRequests.push(_loadResource.bind(null, 'link', url, null));
            return cont();
          }, next);
        },

        async.series.bind(async, loadRequests)
      ], cb);
    });
  }

  function getUserName() {
    userName = sessionStorage.getItem(GO_DRAWINGBOARD_USER_NAME);
    if (userName) {
      alreadyLoaded = true;
      return;
    }

    userName = prompt('What is your name?', 'Guest');
    if (!userName) {
      userName = 'Guest';
    }
    sessionStorage.setItem(GO_DRAWINGBOARD_USER_NAME, userName);

    return;
  }

  function initialize() {
    // we might have to reload if the we are passed a room name so do not
    // load the rest of the resources if the page is being reloaded.
    if (setRoomName()) {
      // get the display name from the user.
      getUserName();

      loadResources(function() {

        // set up listeners on the drawingboard
        //addListeners();

        // set up sharing and components.
        connectToPlatform();
      });
    }
  }

  return {
    initialize: initialize
  };
})();

godrawingboard.initialize();
<commit_msg>fixing room name<commit_after>var godrawingboard = (function() {
  var ASYNC_URL = 'https://cdnjs.cloudflare.com/ajax/libs/async/0.2.7/async.min.js';
  var GO_DRAWINGBOARD_APP = 'https://goinstant.net/GoDrawingBoard.JS/GoDrawingBoard.JS';

  var SCRIPT_URLS = [
    ['https://cdn.goinstant.net/v1/platform.min.js', 'goinstant'],  //PLATFORM
    ['https://cdn.goinstant.net/widgets/user-list/latest/user-list.min.js', 'goinstant.widgets.UserList'],  // USER_LIST
    ['https://cdn.goinstant.net/widgets/click-indicator/latest/click-indicator.min.js', 'goinstant.widgets.ClickIndicator'],  // CLICK_INDICATOR
    ['https://cdn.goinstant.net/widgets/user-colors/latest/user-colors.min.js', 'goinstant.widgets.UserColors'],  // USER_COLORS
    ['https://cdn.goinstant.net/widgets/notifications/latest/notifications.min.js', 'goinstant.widgets.Notifications']  // NOTIFICATIONS
  ];

  var CSS_URLS = [
    'https://cdn.goinstant.net/widgets/user-list/latest/user-list.css',
    'https://cdn.goinstant.net/widgets/click-indicator/latest/click-indicator.css',
    'https://cdn.goinstant.net/widgets/notifications/latest/notifications.css'
  ];

  var ORIGIN_GO_DRAWINGBOARD = 'godrawingboard';
  var GO_DRAWINGBOARD_ID = 'go_drawingboard_room';
  var GO_DRAWINGBOARD_USER_NAME = 'go_drawingboard_user_name';
  var QUERY_REGEX = new RegExp('\\?(.*)\\b' + GO_DRAWINGBOARD_ID + '=([^&#\/]*)(.*)');

  var roomName;
  var userName;
  var alreadyLoaded;
  var drawingboardRoom;
  var slide;
  var query;

  function connectToPlatform(cb) {
    var platform = new goinstant.Platform(GO_DRAWINGBOARD_APP);
    var notifications;

    async.series([
      // connect to GoInstant platform
      platform.connect.bind(platform),

      // create (if needed) the room instance for the drawingboard and
      // join the room and gain access to the drawingboard stat information
      function(next) {
        drawingboardRoom = platform.room(roomName);
        console.log('Joined room: ', roomName);
        drawingboardRoom.join(next);
      },

      // subscribe to any notifications in the presentaiton room.
      function(next) {
        notifications = new goinstant.widgets.Notifications();
        notifications.subscribe(drawingboardRoom, next);
      },

      // set up the user's display name
      function(next) {
        if (alreadyLoaded) {
          return next();
        }

        var publishOpts = {
          room: drawingboardRoom,
          type: 'success',
          message: userName + ' has joined.'
        };

        drawingboardRoom.user(function(err, user, userKey) {
          if (err) {
            return next(err);
          }

          var displayNameKey = userKey.key('displayName');
          displayNameKey.set(userName, function(err) {
            if (err) {
              return next(err);
            }

            // publish a notification of the new user
            notifications.publish(publishOpts, next);
          });
        });
      },

      // select a colour for the current user
      function(next) {
        var opts = {
          room: drawingboardRoom
        };

        var userColors = new goinstant.widgets.UserColors(opts);
        userColors.choose(next);
      },

      // initialize the user list
      function(next) {
        var opts = {
          room: drawingboardRoom,
          position: 'right'
        };

        var userList = new goinstant.widgets.UserList(opts);
        userList.initialize(next);
      },

      // initialize the clicking indicator
      function(next) {
        var opts = {
          room: drawingboardRoom
        };

        var clickIndicator = new goinstant.widgets.ClickIndicator(opts);
        clickIndicator.initialize(next);
      },

      function(next) {
        drawingboardRoom.user(function(err, userObj, userKeyObj) {
          if (err) {
            throw err;
          }
          var defaultBoard = new DrawingBoard.Board('default-board', {
            goinstant: {
              room: drawingboardRoom,
              userKey: userKeyObj
            }
          });
        });
        next();
      },

      // initialize the sharing of the state of the drawingboard.
      initializeSharing
    ], cb);
  }

  function initializeSharing() {

    // We are interested in knowing if there is a new query on the URL when the
    // slide show is loaded. This detects the use of the query parameter in the
    // default slide deck to change the transitions and themes.
    var parser = document.createElement('a');
    parser.href = window.location.toString();

    // Create the sharing URL by adding the roomName as a query parameter to
    // the current window.location.
    if (parser.search) {
      parser.search += '&' + GO_DRAWINGBOARD_ID + '=' + roomName;
    } else {
      parser.search = '?' + GO_DRAWINGBOARD_ID + '=' + roomName;
    }

    // Create Share Button
    addShareButton(parser.href);
  }

  function addShareButton(text) {
    var shareBtn = document.createElement('div');
    var cssBtn = 'display: block; position: fixed; bottom: 1em; left: 0; ' +
      'z-index: 9999; height: 17px; padding: 9px; font-size: 15px; ' +
      'font-family: sans-serif; font-weight: bold; background: white; ' +
      'border-radius: 0 3px 3px 0; border: 1px solid #ccc; ' +
      'text-decoration: none; color: #15A815;';
    var cssURL = 'font-weight: regular;';

    shareBtn.innerHTML = 'Share';
    shareBtn.style.cssText = cssBtn;

    var board = document.getElementsByClassName('board')[0];
    board.parentNode.insertBefore(shareBtn, board);

    shareBtn.onmouseover = function() {
      if (this.poppedOut) {
        return;
      }
      this.poppedOut = true;

      this.innerHTML +=
        '<input id="gi-share-text" type="text" value="' + text +
        '" style="margin: -5px 0 0 15px; padding: 5px; width: 180px;"/>';

      this.style.width = '250px';
      document.getElementById('gi-share-text').select();
    };

    shareBtn.onmouseout = function(evt) {
      if (evt.relatedTarget && evt.relatedTarget.id === 'gi-share-text') {
        return;
      }
      this.poppedOut = false;

      this.innerHTML = 'Share';
      this.style.width = 'auto';
    };
  }

  function setRoomName() {
    // if we have the go-drawingboard room in sessionStorage then just connect to
    // the room and continue with the initialization.
    roomName = sessionStorage.getItem(GO_DRAWINGBOARD_ID);
    if (roomName) {
      return true;
    }

    // if we do not have the name in storage then check to see if the window
    // location contains a query string containing the id of the room.

    // creating an anchor tag and assigning the href to the window location
    // will automatically parse out the URL components ... sweet.
    var parser = document.createElement('a');
    parser.href = window.location.toString();

    var hasRoom = QUERY_REGEX.exec(parser.search);
    var roomId = hasRoom && hasRoom[2];
    if (roomId) {
      roomName = roomId.toString();
      // add the cookie to the document.
      sessionStorage.setItem(GO_DRAWINGBOARD_ID, roomName);

      // regenerate the URI without the go-drawingboard query parameter and reload
      // the page with the new URI.
      var beforeRoom = hasRoom[1];
      if (beforeRoom[beforeRoom.length - 1] === '&') {
        beforeRoom = beforeRoom.slice(0, beforeRoom.lengh - 1);
      }
      var searchStr = beforeRoom + hasRoom[3];
      if (searchStr.length > 0) {
        searchStr = '?' + searchStr;
      }

      parser.search = searchStr;

      // set the new location and discontinue the initialization.
      window.location = parser.href;
      return false;
    }

    // there is no room to join for this drawingboard so simply create a new
    // room and set the cookie in case of future refreshes.
    var id = Math.floor(Math.random() * Math.pow(2, 32));
    roomName = id.toString();
    sessionStorage.setItem(GO_DRAWINGBOARD_ID, roomName);

    return true;
  }

  function loadResources(cb) {
    function _loadResource(type, url, testProperty, loaded) {
      // if the object exists then just return, no need to reload.
      if (window[testProperty]) {
        return loaded();
      }

      // Adding the script tag to the head as suggested before
      var res = document.createElement(type);
      if (type === 'script') {
        res.src = url;
        res.type = 'text/javascript';
      } else {
        res.rel = 'stylesheet';
        res.type = 'text/css';
        res.href = url;
      }

      // callbacks for loaded notification
      function _handleLoadEvent() {
        return loaded();
      }
      res.onreadystatechange = _handleLoadEvent;
      res.onload = _handleLoadEvent;

      // Fire the loading
      var head = document.getElementsByTagName('head')[0];
      head.appendChild(res);
    }

    // get async if it does not already exist.
    _loadResource('script', ASYNC_URL, 'async', function() {

      // Load all the resources in the array. Create the array of resoures
      // to load and then load each.
      var loadRequests = [];
      async.series([
        // add function calls to load all scripts to the load requests array
        function(next) {
          async.each(SCRIPT_URLS, function(params, cont) {
            loadRequests.push(_loadResource.bind(null, 'script', params[0], params[1]));
            return cont();
          }, next);
        },

        // add function calls to load all css to the load requests array
        function(next) {
          async.each(CSS_URLS, function(url, cont) {
            loadRequests.push(_loadResource.bind(null, 'link', url, null));
            return cont();
          }, next);
        },

        async.series.bind(async, loadRequests)
      ], cb);
    });
  }

  function getUserName() {
    userName = sessionStorage.getItem(GO_DRAWINGBOARD_USER_NAME);
    if (userName) {
      alreadyLoaded = true;
      return;
    }

    userName = prompt('What is your name?', 'Guest');
    if (!userName) {
      userName = 'Guest';
    }
    sessionStorage.setItem(GO_DRAWINGBOARD_USER_NAME, userName);

    return;
  }

  function initialize() {
    // we might have to reload if the we are passed a room name so do not
    // load the rest of the resources if the page is being reloaded.
    if (setRoomName()) {
      // get the display name from the user.
      getUserName();

      loadResources(function() {

        // set up listeners on the drawingboard
        //addListeners();

        // set up sharing and components.
        connectToPlatform();
      });
    }
  }

  return {
    initialize: initialize
  };
})();

godrawingboard.initialize();
<|endoftext|>


<commit_before><commit_msg>Fixed an issue where empty paths would sometimes be requested<commit_after><|endoftext|>


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

/**
 * Produces an assignment list of responsible devs. Run from lint.js
 *
 * The Chip Away option provides a quick and easy method to assign devs to their respective repositories
 * for ease in adopting and applying new typescript linting rules.
 * Chip Away will return a markdown formatted checklist with the repository name, responsible dev,
 * and number of errors.
 * Response  format:
 * - [ ] {{REPO}}: @{{GITHUB_USERNAME}} {{NUMBER}} errors in {{NUMBER}} files.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Marla Schulz (PhET Interactive Simulations)
 */

const fs = require( 'fs' );
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const path = require( 'path' );

module.exports = function chipAway( results ) {

  // NOTE: This should never be run in a maintenance mode since this loads a file from phet-info which
  // does not have its SHA tracked as a dependency.
  let responsibleDevs = null;
  try {
    responsibleDevs = JSON.parse( fs.readFileSync( '../phet-info/sim-info/responsible_dev.json' ) );
  }
  catch( e ) {

    // return gracefully if that file was not found or not parseable.
    console.log( 'Could not output chip-away information, due to: ' + e.message );
    return;
  }

  const repos = results.map( result => path.relative( '../', result.filePath ).split( path.sep )[ 0 ] );
  const uniqueRepos = _.uniq( repos ).filter( repo => repo !== 'perennial-alias' );

  // We only want a list of repos that report lint errors
  const reposWithErrors = uniqueRepos.filter( repo => {
    return errorReport( results, repo ).errorCount > 0;
  } );

  // Format chip away assignments. '{{REPO}} @github # errors in # files'
  const assignments = reposWithErrors.map( repo => {
    const fileCount = errorReport( results, repo ).fileCount;
    const errorCount = errorReport( results, repo ).errorCount;

    return ` - [ ] ${repo}: ${responsibleDevs[ repo ].responsibleDevs.join( ', ' )} ${errorCount} errors in ${fileCount} files.`;
  } );
  console.log( assignments.join( '\n' ) );
};

function errorReport( results, repo ) {
  const filteredResults = results.filter( result => path.relative( '../', result.filePath ).split( path.sep )[ 0 ] === repo );
  const fileCount = filteredResults.filter( result => result.errorCount + result.warningCount > 0 ).length;
  const errorCount = _.sum( filteredResults.map( file => file.errorCount + file.warningCount ) );

  return { errorCount: errorCount, fileCount: fileCount };
}
<commit_msg>Output a string message from chipAway, see https://github.com/phetsims/chipper/issues/1262<commit_after>// Copyright 2022, University of Colorado Boulder

/**
 * Produces an assignment list of responsible devs. Run from lint.js
 *
 * The Chip Away option provides a quick and easy method to assign devs to their respective repositories
 * for ease in adopting and applying new typescript linting rules.
 * Chip Away will return a markdown formatted checklist with the repository name, responsible dev,
 * and number of errors.
 * Response  format:
 * - [ ] {{REPO}}: @{{GITHUB_USERNAME}} {{NUMBER}} errors in {{NUMBER}} files.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Marla Schulz (PhET Interactive Simulations)
 */

const fs = require( 'fs' );
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const path = require( 'path' );

module.exports = results => {

  // NOTE: This should never be run in a maintenance mode since this loads a file from phet-info which
  // does not have its SHA tracked as a dependency.
  let responsibleDevs = null;
  try {
    responsibleDevs = JSON.parse( fs.readFileSync( '../phet-info/sim-info/responsible_dev.json' ) );
  }
  catch( e ) {

    // return gracefully if that file was not found or not parseable.
    return 'Could not output chip-away information, due to: ' + e.message;
  }

  const repos = results.map( result => path.relative( '../', result.filePath ).split( path.sep )[ 0 ] );
  const uniqueRepos = _.uniq( repos ).filter( repo => repo !== 'perennial-alias' );

  // We only want a list of repos that report lint errors
  const reposWithErrors = uniqueRepos.filter( repo => {
    return errorReport( results, repo ).errorCount > 0;
  } );

  // Format chip away assignments. '{{REPO}} @github # errors in # files'
  const assignments = reposWithErrors.map( repo => {
    const fileCount = errorReport( results, repo ).fileCount;
    const errorCount = errorReport( results, repo ).errorCount;

    return ` - [ ] ${repo}: ${responsibleDevs[ repo ].responsibleDevs.join( ', ' )} ${errorCount} errors in ${fileCount} files.`;
  } );

  return assignments.join( '\n' );
};

function errorReport( results, repo ) {
  const filteredResults = results.filter( result => path.relative( '../', result.filePath ).split( path.sep )[ 0 ] === repo );
  const fileCount = filteredResults.filter( result => result.errorCount + result.warningCount > 0 ).length;
  const errorCount = _.sum( filteredResults.map( file => file.errorCount + file.warningCount ) );

  return { errorCount: errorCount, fileCount: fileCount };
}
<|endoftext|>


<commit_before><commit_msg>Improved root decorator code slightly, added JSDoc<commit_after><|endoftext|>


<commit_before>
/**
 * Various helper functions for the application.
 */
clientApp.service('clientAppHelper', function($http, $location, $anchorScroll, utils,
		ProgressbarService, advancedSettings, SERVICES_CONFIG, credentials, GENERAL_CONSTANTS) {
	var helper = this;

	/**
	 * Based on the selected environment and service, determine the correct URL to use.
	 */
	helper.configureServiceUrl = function(selectedEnvironment, selectedService, parameter) {
		var url = "";

		//Determine the endpoint based on selected Environment and Service.
		for (var endpoint of SERVICES_CONFIG.endpoints) {
			if (endpoint.service === selectedService && (endpoint.env === "" || endpoint.env === selectedEnvironment)) {
				url = endpoint.url;
				break;
			}
		}

		//Remove dashes from the optional parameter and add it to the URL.
		var placeholder = SERVICES_CONFIG.placeholder;
		if (parameter) {
			placeholder = utils.replaceAll(parameter, "-", "");
		}
		if (url) {
			url = url.replace("{placeholder}", placeholder);
		}
		return url;
	};

	/**
	 * Determine the service endpoint to use and call the service.
	 */
	helper.configureAndCallService = function($scope, token) {
		$scope.authenticationToken = token;

		//Determine the configured service endpoint.
		if (advancedSettings.requestUrl) {
			//If the user has entered a specific endpoint, just use that.
			$scope.requestUrl = advancedSettings.requestUrl;
		} else {
			$scope.requestUrl = helper.configureServiceUrl($scope.selectedEnvironment, $scope.selectedService, $scope.parameter);
		}

		//Call the endpoint.
		$scope.progress = ProgressbarService.getProgressState('IN_PROGRESS');
		helper.callService($scope);
	};

	/**
	 * Call the specified endpoint and update the UI.
	 */
	helper.callService = function($scope) {
		var requestConfig = { headers: {} };
		if (advancedSettings.autoAuthenticate) {
			requestConfig = { headers: {
				'Authorization': $scope.authenticationToken,
				'ApplicationId': advancedSettings.credentials.appId
			}};
		}

		//Determine the request method to use (GET/POST/PUT/DELETE/HEAD/PATCH).
		var promise;
		$scope.timerStart = Date.now();
		switch (advancedSettings.requestMethod) {
			case "post":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.post($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "put":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.put($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "patch":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.patch($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "delete":
				promise = $http.delete($scope.requestUrl, requestConfig);
				break;
			case "head":
				promise = $http.head($scope.requestUrl, requestConfig);
				break;
			case "get":
				/* falls through */
			default:
				promise = $http.get($scope.requestUrl, requestConfig);
		}

		//Handle the response.
		promise.then(function(success) {
			helper.populateView($scope, success);
			helper.displayView($scope);
			helper.storeResponseDetails($scope, success.data);
		}, function(error) {
			helper.populateView($scope, error);
			helper.displayView($scope);
		});
	};

	/**
	 * Add request headers indicating the type of payload. Used by POST/PUT operations.
	 */
	helper.addPayloadHeaders = function(headers) {
		headers['Accept'] = "application/json";
		headers['Content-Type'] = "application/json";
	};

	/**
	 * Persist the request/response so we have a history of them.
	 * NOTE: Currently 2 methods are used to persist the data. At some point, we should standardise on just one.
	 */
	helper.storeResponseDetails = function($scope, response) {
		//Construct the new entry.
		var entry = {
			date: Date(),
			request: $scope.requestUrl,
			response: helper.excludeLargeObjects(response),
			method: advancedSettings.requestMethod,
			timer: $scope.timerEnd - $scope.timerStart
		};
		if (advancedSettings.requestMethod === 'post' || advancedSettings.requestMethod === 'put') {
			entry['payload'] = advancedSettings.payload;
		}

		//Persist it using LocalStorage.
		var key = "restclient.history." + Date.now();
		localStorage[key] = JSON.stringify(entry);

		//Also use Chrome Storage to persist it. Supports objects.
		if (typeof chrome !== 'undefined') {
			var keyValue = {};
			keyValue[key] = entry;
			chrome.storage.local.set(keyValue);
		}
	};

	/**
	 * Storing large responses (e.g. base64 encoded PDFs) can lead to performance issues
	 * so we explicitly exclude them.
	 */
	helper.excludeLargeObjects = function(response) {
		var responseSize = sizeof(response);
		if (responseSize > GENERAL_CONSTANTS.MAX_OBJECT_SIZE) {
			return "The response was too large to store. Only responses less than "
				+ GENERAL_CONSTANTS.MAX_OBJECT_SIZE + " bytes in size will be stored. This response was approx "
				+ responseSize + " bytes.";
		}
		return response;
	};

	/**
	 * Update the UI with the data received from the service.
	 */
	helper.populateView = function($scope, response) {
		$scope.timerEnd = Date.now();
		$scope.progress = ProgressbarService.getProgressState('COMPLETE');
		response.headers().status = response.status;
		$scope.responseBody = JSON.stringify(response.data, null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
		$scope.responseHeaders = JSON.stringify(response.headers(), null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
		$scope.requestHeaders = JSON.stringify(response.config, null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
	};

	/**
	 * Show the view and automatically scroll down to it.
	 */
	helper.displayView = function($scope) {
		$scope.displayResponse = true;
		$scope.processing = false;

		//Attempt to scroll to the response. This has issues.
		var old = $location.hash();
		$location.hash('response');
		$anchorScroll();
		$location.hash(old);
	};

	/**
	 * Delete cookies that can interfere with authentication.
	 * NOTE: This will likely mess up any open OAM application sessions.
	 */
	helper.deleteCookies = function() {
		if (typeof chrome !== 'undefined') {
			chrome.cookies.remove({"url": "http://dnb.com", "name": "userid"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "dnb_loginid"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "redirect"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "ObSSOCookie"});
		}
	};

	/**
	 * Perform some Chrome specific operations that will only work in Chrome.
	 */
	helper.performChromeOperations = function($scope) {
		if (typeof chrome !== 'undefined') {
			$scope.chromeSupport = true;
			$scope.version = "v" + chrome.runtime.getManifest()['version'];
			helper.addUserDefinedServices($scope);
			helper.retrieveAndSetCredentials($scope.selectedEnvironment);
		}
	};

	/**
	 * Check if the necessary credentials have been provided.
	 */
	helper.areCredentialsPresent = function() {
		return advancedSettings.credentials
			&& advancedSettings.credentials.appId
			&& advancedSettings.credentials.userId
			&& advancedSettings.credentials.password;
	};

	/**
	 * Persist the credentials if they've changed.
	 */
	helper.persistCredentials = function(env) {
		//Check if the credentials have changed.
		var initialCredentials = credentials[env];
		var currentCredentials = advancedSettings.credentials;
		if (typeof initialCredentials === 'undefined'
				|| initialCredentials.appId !== currentCredentials.appId
				|| initialCredentials.userId !== currentCredentials.userId
				|| initialCredentials.password !== currentCredentials.password) {
			//Persist the updated credentials (include some other relevant data).
			currentCredentials.date = Date();
			currentCredentials.env = env;
			var keyValue = {};
			keyValue["restclient.credentials." + env] = currentCredentials;
			chrome.storage.sync.set(keyValue);
			credentials[env] = currentCredentials;
		}
	};

	/**
	 * Retrieve the stored credentials and set them for the current environment.
	 */
	helper.retrieveAndSetCredentials = function(selectedEnvironment) {
		var keys = ["restclient.credentials.qa", "restclient.credentials.stg", "restclient.credentials.prod"];
		chrome.storage.sync.get(keys, function (environments) {
			for (var key in environments) {
				//Use the stored credentials object to set the credentials for each environment.
				var currentCredentials = environments[key];
				credentials[currentCredentials.env] = currentCredentials;
			}

			//Set the initial credential in the UI.
			//Clone rather than pass by reference so we can determine later if the credentials have been modified.
			if (credentials[selectedEnvironment]) {
				advancedSettings.credentials = JSON.parse(JSON.stringify(credentials[selectedEnvironment]));
			}
		});
	};

	/**
	 * Add custom services that the user has previously saved.
	 */
	helper.addUserDefinedServices = function($scope) {
		chrome.storage.sync.get(null, function (services) {
			for (var key in services) {
				var service = services[key];
				if (service.serviceName && service.endpoint) {
					SERVICES_CONFIG.services.unshift(service.serviceName);
					SERVICES_CONFIG.endpoints.unshift(service.endpoint);
				}
			}
			$scope.$apply();
		});
	};

	/**
	 * Retrieve the Custom Services. Avoids going to Chrome Storage again.
	 */
	helper.getCustomServices = function() {
		var customServices = [];
		for (var service of SERVICES_CONFIG.services) {
			if (service.group === "Custom") {
				customServices.push(service);
			}
		}
		return customServices;
	};

	/**
	 * Return the service object with the specified id from the array.
	 */
	helper.findServiceById = function(id, services) {
		var serviceToDelete = {};
		for (var service of services) {
			if (service.id === id) {
				serviceToDelete = service;
				break;
			}
		}
		return serviceToDelete;
	};
});
<commit_msg>Store error responses, not just successes<commit_after>
/**
 * Various helper functions for the application.
 */
clientApp.service('clientAppHelper', function($http, $location, $anchorScroll, utils,
		ProgressbarService, advancedSettings, SERVICES_CONFIG, credentials, GENERAL_CONSTANTS) {
	var helper = this;

	/**
	 * Based on the selected environment and service, determine the correct URL to use.
	 */
	helper.configureServiceUrl = function(selectedEnvironment, selectedService, parameter) {
		var url = "";

		//Determine the endpoint based on selected Environment and Service.
		for (var endpoint of SERVICES_CONFIG.endpoints) {
			if (endpoint.service === selectedService && (endpoint.env === "" || endpoint.env === selectedEnvironment)) {
				url = endpoint.url;
				break;
			}
		}

		//Remove dashes from the optional parameter and add it to the URL.
		var placeholder = SERVICES_CONFIG.placeholder;
		if (parameter) {
			placeholder = utils.replaceAll(parameter, "-", "");
		}
		if (url) {
			url = url.replace("{placeholder}", placeholder);
		}
		return url;
	};

	/**
	 * Determine the service endpoint to use and call the service.
	 */
	helper.configureAndCallService = function($scope, token) {
		$scope.authenticationToken = token;

		//Determine the configured service endpoint.
		if (advancedSettings.requestUrl) {
			//If the user has entered a specific endpoint, just use that.
			$scope.requestUrl = advancedSettings.requestUrl;
		} else {
			$scope.requestUrl = helper.configureServiceUrl($scope.selectedEnvironment, $scope.selectedService, $scope.parameter);
		}

		//Call the endpoint.
		$scope.progress = ProgressbarService.getProgressState('IN_PROGRESS');
		helper.callService($scope);
	};

	/**
	 * Call the specified endpoint and update the UI.
	 */
	helper.callService = function($scope) {
		var requestConfig = { headers: {} };
		if (advancedSettings.autoAuthenticate) {
			requestConfig = { headers: {
				'Authorization': $scope.authenticationToken,
				'ApplicationId': advancedSettings.credentials.appId
			}};
		}

		//Determine the request method to use (GET/POST/PUT/DELETE/HEAD/PATCH).
		var promise;
		$scope.timerStart = Date.now();
		switch (advancedSettings.requestMethod) {
			case "post":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.post($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "put":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.put($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "patch":
				helper.addPayloadHeaders(requestConfig.headers);
				promise = $http.patch($scope.requestUrl, advancedSettings.payload, requestConfig);
				break;
			case "delete":
				promise = $http.delete($scope.requestUrl, requestConfig);
				break;
			case "head":
				promise = $http.head($scope.requestUrl, requestConfig);
				break;
			case "get":
				/* falls through */
			default:
				promise = $http.get($scope.requestUrl, requestConfig);
		}

		//Handle the service response.
		promise.then(function(success) {
			helper.handleResponse($scope, success);
		}, function(error) {
			helper.handleResponse($scope, error);
		});
	};

	/**
	 * Add request headers indicating the type of payload. Used by POST/PUT operations.
	 */
	helper.addPayloadHeaders = function(headers) {
		headers['Accept'] = "application/json";
		headers['Content-Type'] = "application/json";
	};

	/**
	 * Handle the service response. Update UI, store details etc.
	 */
	helper.handleResponse = function($scope, response) {
		helper.populateView($scope, response);
		helper.displayView($scope);
		helper.storeResponseDetails($scope, response.data);
	};

	/**
	 * Persist the request/response so we have a history of them.
	 * NOTE: Currently 2 methods are used to persist the data. At some point, we should standardise on just one.
	 */
	helper.storeResponseDetails = function($scope, response) {
		//Construct the new entry.
		var entry = {
			date: Date(),
			request: $scope.requestUrl,
			response: helper.excludeLargeObjects(response),
			method: advancedSettings.requestMethod,
			timer: $scope.timerEnd - $scope.timerStart
		};
		if (advancedSettings.requestMethod === 'post' || advancedSettings.requestMethod === 'put') {
			entry['payload'] = advancedSettings.payload;
		}

		//Persist it using LocalStorage.
		var key = "restclient.history." + Date.now();
		localStorage[key] = JSON.stringify(entry);

		//Also use Chrome Storage to persist it. Supports objects.
		if (typeof chrome !== 'undefined') {
			var keyValue = {};
			keyValue[key] = entry;
			chrome.storage.local.set(keyValue);
		}
	};

	/**
	 * Storing large responses (e.g. base64 encoded PDFs) can lead to performance issues
	 * so we explicitly exclude them.
	 */
	helper.excludeLargeObjects = function(response) {
		var responseSize = sizeof(response);
		if (responseSize > GENERAL_CONSTANTS.MAX_OBJECT_SIZE) {
			return "The response was too large to store. Only responses less than "
				+ GENERAL_CONSTANTS.MAX_OBJECT_SIZE + " bytes in size will be stored. This response was approx "
				+ responseSize + " bytes.";
		}
		return response;
	};

	/**
	 * Update the UI with the data received from the service.
	 */
	helper.populateView = function($scope, response) {
		$scope.timerEnd = Date.now();
		$scope.progress = ProgressbarService.getProgressState('COMPLETE');
		response.headers().status = response.status;
		$scope.responseBody = JSON.stringify(response.data, null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
		$scope.responseHeaders = JSON.stringify(response.headers(), null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
		$scope.requestHeaders = JSON.stringify(response.config, null, GENERAL_CONSTANTS.INDENTATION_LEVEL);
	};

	/**
	 * Show the view and automatically scroll down to it.
	 */
	helper.displayView = function($scope) {
		$scope.displayResponse = true;
		$scope.processing = false;

		//Attempt to scroll to the response. This has issues.
		var old = $location.hash();
		$location.hash('response');
		$anchorScroll();
		$location.hash(old);
	};

	/**
	 * Delete cookies that can interfere with authentication.
	 * NOTE: This will likely mess up any open OAM application sessions.
	 */
	helper.deleteCookies = function() {
		if (typeof chrome !== 'undefined') {
			chrome.cookies.remove({"url": "http://dnb.com", "name": "userid"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "dnb_loginid"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "redirect"});
			chrome.cookies.remove({"url": "http://dnb.com", "name": "ObSSOCookie"});
		}
	};

	/**
	 * Perform some Chrome specific operations that will only work in Chrome.
	 */
	helper.performChromeOperations = function($scope) {
		if (typeof chrome !== 'undefined') {
			$scope.chromeSupport = true;
			$scope.version = "v" + chrome.runtime.getManifest()['version'];
			helper.addUserDefinedServices($scope);
			helper.retrieveAndSetCredentials($scope.selectedEnvironment);
		}
	};

	/**
	 * Check if the necessary credentials have been provided.
	 */
	helper.areCredentialsPresent = function() {
		return advancedSettings.credentials
			&& advancedSettings.credentials.appId
			&& advancedSettings.credentials.userId
			&& advancedSettings.credentials.password;
	};

	/**
	 * Persist the credentials if they've changed.
	 */
	helper.persistCredentials = function(env) {
		//Check if the credentials have changed.
		var initialCredentials = credentials[env];
		var currentCredentials = advancedSettings.credentials;
		if (typeof initialCredentials === 'undefined'
				|| initialCredentials.appId !== currentCredentials.appId
				|| initialCredentials.userId !== currentCredentials.userId
				|| initialCredentials.password !== currentCredentials.password) {
			//Persist the updated credentials (include some other relevant data).
			currentCredentials.date = Date();
			currentCredentials.env = env;
			var keyValue = {};
			keyValue["restclient.credentials." + env] = currentCredentials;
			chrome.storage.sync.set(keyValue);
			credentials[env] = currentCredentials;
		}
	};

	/**
	 * Retrieve the stored credentials and set them for the current environment.
	 */
	helper.retrieveAndSetCredentials = function(selectedEnvironment) {
		var keys = ["restclient.credentials.qa", "restclient.credentials.stg", "restclient.credentials.prod"];
		chrome.storage.sync.get(keys, function (environments) {
			for (var key in environments) {
				//Use the stored credentials object to set the credentials for each environment.
				var currentCredentials = environments[key];
				credentials[currentCredentials.env] = currentCredentials;
			}

			//Set the initial credential in the UI.
			//Clone rather than pass by reference so we can determine later if the credentials have been modified.
			if (credentials[selectedEnvironment]) {
				advancedSettings.credentials = JSON.parse(JSON.stringify(credentials[selectedEnvironment]));
			}
		});
	};

	/**
	 * Add custom services that the user has previously saved.
	 */
	helper.addUserDefinedServices = function($scope) {
		chrome.storage.sync.get(null, function (services) {
			for (var key in services) {
				var service = services[key];
				if (service.serviceName && service.endpoint) {
					SERVICES_CONFIG.services.unshift(service.serviceName);
					SERVICES_CONFIG.endpoints.unshift(service.endpoint);
				}
			}
			$scope.$apply();
		});
	};

	/**
	 * Retrieve the Custom Services. Avoids going to Chrome Storage again.
	 */
	helper.getCustomServices = function() {
		var customServices = [];
		for (var service of SERVICES_CONFIG.services) {
			if (service.group === "Custom") {
				customServices.push(service);
			}
		}
		return customServices;
	};

	/**
	 * Return the service object with the specified id from the array.
	 */
	helper.findServiceById = function(id, services) {
		var serviceToDelete = {};
		for (var service of services) {
			if (service.id === id) {
				serviceToDelete = service;
				break;
			}
		}
		return serviceToDelete;
	};
});
<|endoftext|>


<commit_before><commit_msg>fix tests for creating hook <commit_after><|endoftext|>


<commit_before>// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes
var path = require('canonical-path');
var Q = require('q');
var _ = require('lodash');
var jsdom = require("jsdom");
var fs = require("fs");
var globby = require('globby');
var mkdirp = require('mkdirp');

var indexHtmlTranslator = require('./indexHtmlTranslator');
var regionExtractor = require('../doc-shredder/regionExtractor');

module.exports = {
  buildPlunkers: buildPlunkers
};

function buildPlunkers(basePath, destPath, options) {
  var errFn = options.errFn || function(e) { console.log(e); };
  var configExtns = ['plnkr.json', '*plnkr.json'];
  var gpaths = configExtns.map(function(extn) {
    return path.join(basePath, '**/' + extn);
  });
  var fileNames = globby.sync(gpaths, { ignore: "**/node_modules/**"});
  fileNames.forEach(function(configFileName) {
    try {
      buildPlunkerFrom(configFileName, basePath, destPath);
    } catch (e) {
      errFn(e);
    }
  });
}

// config has
//   files: [] - optional array of globs - defaults to all js, ts, html, json, css and md files (with certain files removed)
//   description: optional string - description of this plunker - defaults to the title in the index.html page.
//   tags: [] - optional array of strings
//   main: string - filename of what will become index.html in the plunker - defaults to index.html
function buildPlunkerFrom(configFileName, basePath, destPath ) {
  // replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name;
  var outputFileName = configFileName.substr(0, configFileName.length - 'plnkr.json'.length) + 'plnkr.no-link.html';
  var altFileName;
  if (destPath && destPath.length > 0) {
    var partPath = path.dirname(path.relative(basePath, outputFileName));
    var altFileName = path.join(destPath, partPath, path.basename(outputFileName)).replace('.no-link.', '.');
  }
  try {
    var config = initConfigAndCollectFileNames(configFileName);
    var postData = createPostData(config);
    var html = createPlunkerHtml(postData);
    fs.writeFileSync(outputFileName, html, 'utf-8');
    if (altFileName) {
      var altDirName = path.dirname(altFileName);
      if (!fs.existsSync(altDirName)) {
        mkdirp.sync(altDirName);
      }
      fs.writeFileSync(altFileName, html, 'utf-8');
    }
  } catch (e) {
    // if we fail delete the outputFile if it exists because it is an old one.
    if (existsSync(outputFileName)) {
      fs.unlinkSync(outputFileName);
    }
    if (altFileName && existsSync(altFileName)) {
      fs.unlinkSync(altFileName);
    }
    throw e;
  }
}

function initConfigAndCollectFileNames(configFileName) {
  var basePath = path.dirname(configFileName);
  var configSrc = fs.readFileSync(configFileName, 'utf-8');
  try {
    var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {};
  } catch (e) {
    throw new Error("Plunker config - unable to parse json file: " + configFileName + '\n  ' + e);
  }

  var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png'];
  if (config.files) {
    if (config.files.length > 0) {
      if (config.files[0].substr(0, 1) == '!') {
        config.files = defaultIncludes.concat(config.files);
      }
    }
  } else {
    config.files = defaultIncludes;
  }
  var gpaths = config.files.map(function(fileName) {
    fileName = fileName.trim();
    if (fileName.substr(0,1) == '!') {
      return "!" + path.join(basePath, fileName.substr(1));
    } else {
      return path.join(basePath, fileName);
    }
  });
  // var defaultExcludes = [ '!**/node_modules/**','!**/typings/**','!**/tsconfig.json', '!**/*plnkr.json', '!**/*plnkr.html', '!**/*plnkr.no-link.html' ];
  var defaultExcludes = [
    '!**/typings/**',
    '!**/tsconfig.json',
    '!**/*plnkr.*',
    '!**/package.json',
    '!**/example-config.json',
    '!**/*.spec.*'
   ];
  Array.prototype.push.apply(gpaths, defaultExcludes);

  config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] });
  config.basePath = basePath;
  return config;
}

function createPostData(config) {
  var postData = {};
  config.fileNames.forEach(function(fileName) {
    var content;
    var extn = path.extname(fileName);
    if (extn == '.png') {
      content = encodeBase64(fileName);
      fileName = fileName.substr(0, fileName.length - 4) + '.base64.png'
    } else {
      content = fs.readFileSync(fileName, 'utf-8');
    }
    // var escapedValue = escapeHtml(content);

    var relativeFileName = path.relative(config.basePath, fileName);

    if (relativeFileName == config.main) {
      relativeFileName = 'index.html';
    }

    if (relativeFileName == 'index.html') {
      content = indexHtmlTranslator.translate(content);
      if (config.description == null) {
        // set config.description to title from index.html
        var matches = /<title>(.*)<\/title>/.exec(content);
        if (matches) {
          config.description = matches[1];
        }
      }
    }
    content = regionExtractor.removeDocTags(content, extn.substr(1));

    postData['files[' + relativeFileName + ']'] = content;
  });
  postData['files[license.md]'] = fs.readFileSync(path.join(__dirname, "license.md"));

  var tags = ['angular2', 'example'].concat(config.tags || []);
  tags.forEach(function(tag,ix) {
    postData['tags[' + ix + ']'] = tag;
  });
  // postData['tags[0]'] = "angular2";
  // postData['tags[1]'] = "example";
  postData.private = true;

  postData.description = "Angular 2 Example - " + config.description;
  return postData;
}

function existsSync(filename) {
  try {
    fs.accessSync(filename);
    return true;
  } catch(ex) {
    return false;
  }
}

function encodeBase64(file) {
  // read binary data
  var bitmap = fs.readFileSync(file);
  // convert binary data to base64 encoded string
  return new Buffer(bitmap).toString('base64');
}

function createPlunkerHtml(postData) {
  useNewWindow = false;
  var baseHtml = createBasePlunkerHtml(useNewWindow);
  var doc = jsdom.jsdom(baseHtml);
  var form = doc.querySelector('form');
  _.forEach(postData, function(value, key) {
    var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">');
    ele.setAttribute('value', value);
    form.appendChild(ele)
  });
  var html = doc.documentElement.outerHTML;
  return html;
}

function createBasePlunkerHtml(useNewWindow) {
  var url = 'http://plnkr.co/edit/?p=preview';
  // If the form posts to target="_blank", pop-up blockers can cause it not to work.
  // If a user choses to bypass pop-up blocker one time and click the link, they will arrive at
  // a new default plnkr, not a plnkr with the desired template.  Given this undesired behavior,
  // some may still want to open the plnk in a new window by opting-in via ctrl+click.  The
  // newWindow param allows for this possibility.
  var target = useNewWindow ? '_blank' : '_self';
  var html = '<!DOCTYPE html>'
  html += '<form id="mainForm" method="post" action="' + url + '" target="' + target + '">'

  // html += '
<button id="formButton" type="submit">Create Plunker</button>
'
  // html += '</form>'
  html +=  '</form>'
  html += '';
  return html;
}

function htmlToElement(document, html) {
  var div = document.createElement('div');
  div.innerHTML = html;
  return div.firstChild;
}

// not currently used.
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "<")
    .replace(/>/g, ">")
    .replace(/"/g, """)
    .replace(/'/g, "'");
}

//// Old version - no longer used
//function createPlunkerHtmlAsync(basePath, postData) {
//
//  useNewWindow = false;
//  jsdom.env({
//    html: createBasePlunkerHtml(useNewWindow),
//    done: function (err, window) {
//      var doc = window.document;
//      var form = doc.querySelector('form');
//
//      _.forEach(postData, function(value, key) {
//        var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">');
//        ele.setAttribute('value', value);
//        form.appendChild(ele)
//      });
//      var html = doc.documentElement.outerHTML;
//      var outputFn = path.join(basePath, "plnkr.html");
//      fs.writeFileSync(outputFn, html, 'utf-8' );
//    }
//  });
//}<commit_msg>docs(copyright): Add Copyright and license info to each file in the plunker. closes #671<commit_after>// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes
var path = require('canonical-path');
var Q = require('q');
var _ = require('lodash');
var jsdom = require("jsdom");
var fs = require("fs");
var globby = require('globby');
var mkdirp = require('mkdirp');

var indexHtmlTranslator = require('./indexHtmlTranslator');
var regionExtractor = require('../doc-shredder/regionExtractor');
var COPYRIGHT, COPYRIGHT_JS, COPYRIGHT_HTML;

module.exports = {
  buildPlunkers: buildPlunkers
};

buildCopyrightStrings();

function buildCopyrightStrings() {
  var COPYRIGHT = 'Copyright 2016 Google Inc. All Rights Reserved.\n'
    + 'Use of this source code is governed by an MIT-style license that\n'
    + 'can be found in the LICENSE file at http://angular.io/license';
  var pad = '\n\n';
  COPYRIGHT_JS_CSS = pad + '/*\n' + COPYRIGHT + '\n*/';
  COPYRIGHT_HTML = pad + ''
}

function buildPlunkers(basePath, destPath, options) {
  var errFn = options.errFn || function(e) { console.log(e); };
  var configExtns = ['plnkr.json', '*plnkr.json'];
  var gpaths = configExtns.map(function(extn) {
    return path.join(basePath, '**/' + extn);
  });
  var fileNames = globby.sync(gpaths, { ignore: "**/node_modules/**"});
  fileNames.forEach(function(configFileName) {
    try {
      buildPlunkerFrom(configFileName, basePath, destPath);
    } catch (e) {
      errFn(e);
    }
  });
}

// config has
//   files: [] - optional array of globs - defaults to all js, ts, html, json, css and md files (with certain files removed)
//   description: optional string - description of this plunker - defaults to the title in the index.html page.
//   tags: [] - optional array of strings
//   main: string - filename of what will become index.html in the plunker - defaults to index.html
function buildPlunkerFrom(configFileName, basePath, destPath ) {
  // replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name;
  var outputFileName = configFileName.substr(0, configFileName.length - 'plnkr.json'.length) + 'plnkr.no-link.html';
  var altFileName;
  if (destPath && destPath.length > 0) {
    var partPath = path.dirname(path.relative(basePath, outputFileName));
    var altFileName = path.join(destPath, partPath, path.basename(outputFileName)).replace('.no-link.', '.');
  }
  try {
    var config = initConfigAndCollectFileNames(configFileName);
    var postData = createPostData(config);
    var html = createPlunkerHtml(postData);
    fs.writeFileSync(outputFileName, html, 'utf-8');
    if (altFileName) {
      var altDirName = path.dirname(altFileName);
      if (!fs.existsSync(altDirName)) {
        mkdirp.sync(altDirName);
      }
      fs.writeFileSync(altFileName, html, 'utf-8');
    }
  } catch (e) {
    // if we fail delete the outputFile if it exists because it is an old one.
    if (existsSync(outputFileName)) {
      fs.unlinkSync(outputFileName);
    }
    if (altFileName && existsSync(altFileName)) {
      fs.unlinkSync(altFileName);
    }
    throw e;
  }
}

function initConfigAndCollectFileNames(configFileName) {
  var basePath = path.dirname(configFileName);
  var configSrc = fs.readFileSync(configFileName, 'utf-8');
  try {
    var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {};
  } catch (e) {
    throw new Error("Plunker config - unable to parse json file: " + configFileName + '\n  ' + e);
  }

  var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png'];
  if (config.files) {
    if (config.files.length > 0) {
      if (config.files[0].substr(0, 1) == '!') {
        config.files = defaultIncludes.concat(config.files);
      }
    }
  } else {
    config.files = defaultIncludes;
  }
  var gpaths = config.files.map(function(fileName) {
    fileName = fileName.trim();
    if (fileName.substr(0,1) == '!') {
      return "!" + path.join(basePath, fileName.substr(1));
    } else {
      return path.join(basePath, fileName);
    }
  });
  // var defaultExcludes = [ '!**/node_modules/**','!**/typings/**','!**/tsconfig.json', '!**/*plnkr.json', '!**/*plnkr.html', '!**/*plnkr.no-link.html' ];
  var defaultExcludes = [
    '!**/typings/**',
    '!**/tsconfig.json',
    '!**/*plnkr.*',
    '!**/package.json',
    '!**/example-config.json',
    '!**/*.spec.*'
   ];
  Array.prototype.push.apply(gpaths, defaultExcludes);

  config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] });
  config.basePath = basePath;
  return config;
}

function createPostData(config) {
  var postData = {};
  config.fileNames.forEach(function(fileName) {
    var content;
    var extn = path.extname(fileName);
    if (extn == '.png') {
      content = encodeBase64(fileName);
      fileName = fileName.substr(0, fileName.length - 4) + '.base64.png'
    } else {
      content = fs.readFileSync(fileName, 'utf-8');
    }

    if (extn == '.js' || extn == '.ts' || extn == '.css') {
      content = content + COPYRIGHT_JS_CSS;
    } else if (extn == '.html') {
      content = content + COPYRIGHT_HTML;
    }
    // var escapedValue = escapeHtml(content);

    var relativeFileName = path.relative(config.basePath, fileName);

    if (relativeFileName == config.main) {
      relativeFileName = 'index.html';
    }

    if (relativeFileName == 'index.html') {
      content = indexHtmlTranslator.translate(content);
      if (config.description == null) {
        // set config.description to title from index.html
        var matches = /<title>(.*)<\/title>/.exec(content);
        if (matches) {
          config.description = matches[1];
        }
      }
    }
    content = regionExtractor.removeDocTags(content, extn.substr(1));

    postData['files[' + relativeFileName + ']'] = content;
  });

  // Leave here in case we want to add a md file later.
  // postData['files[license.md]'] = fs.readFileSync(path.join(__dirname, "license.md"));

  var tags = ['angular2', 'example'].concat(config.tags || []);
  tags.forEach(function(tag,ix) {
    postData['tags[' + ix + ']'] = tag;
  });
  // postData['tags[0]'] = "angular2";
  // postData['tags[1]'] = "example";
  postData.private = true;

  postData.description = "Angular 2 Example - " + config.description;
  return postData;
}

function existsSync(filename) {
  try {
    fs.accessSync(filename);
    return true;
  } catch(ex) {
    return false;
  }
}

function encodeBase64(file) {
  // read binary data
  var bitmap = fs.readFileSync(file);
  // convert binary data to base64 encoded string
  return new Buffer(bitmap).toString('base64');
}

function createPlunkerHtml(postData) {
  useNewWindow = false;
  var baseHtml = createBasePlunkerHtml(useNewWindow);
  var doc = jsdom.jsdom(baseHtml);
  var form = doc.querySelector('form');
  _.forEach(postData, function(value, key) {
    var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">');
    ele.setAttribute('value', value);
    form.appendChild(ele)
  });
  var html = doc.documentElement.outerHTML;
  return html;
}

function createBasePlunkerHtml(useNewWindow) {
  var url = 'http://plnkr.co/edit/?p=preview';
  // If the form posts to target="_blank", pop-up blockers can cause it not to work.
  // If a user choses to bypass pop-up blocker one time and click the link, they will arrive at
  // a new default plnkr, not a plnkr with the desired template.  Given this undesired behavior,
  // some may still want to open the plnk in a new window by opting-in via ctrl+click.  The
  // newWindow param allows for this possibility.
  var target = useNewWindow ? '_blank' : '_self';
  var html = '<!DOCTYPE html>'
  html += '<form id="mainForm" method="post" action="' + url + '" target="' + target + '">'

  // html += '
<button id="formButton" type="submit">Create Plunker</button>
'
  // html += '</form>'
  html +=  '</form>'
  html += '';
  return html;
}

function htmlToElement(document, html) {
  var div = document.createElement('div');
  div.innerHTML = html;
  return div.firstChild;
}

// not currently used.
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "<")
    .replace(/>/g, ">")
    .replace(/"/g, """)
    .replace(/'/g, "'");
}

//// Old version - no longer used
//function createPlunkerHtmlAsync(basePath, postData) {
//
//  useNewWindow = false;
//  jsdom.env({
//    html: createBasePlunkerHtml(useNewWindow),
//    done: function (err, window) {
//      var doc = window.document;
//      var form = doc.querySelector('form');
//
//      _.forEach(postData, function(value, key) {
//        var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">');
//        ele.setAttribute('value', value);
//        form.appendChild(ele)
//      });
//      var html = doc.documentElement.outerHTML;
//      var outputFn = path.join(basePath, "plnkr.html");
//      fs.writeFileSync(outputFn, html, 'utf-8' );
//    }
//  });
//}<|endoftext|>


<commit_before><commit_msg>Prevent “false successes” in e2e tests<commit_after><|endoftext|>


<commit_before>/**
 * Copyright (c) UNA, Inc - https://una.io
 * MIT License - https://opensource.org/licenses/MIT
 *
 * @defgroup    UnaCore UNA Core
 * @{
 */

function BxDolMenuMoreAuto(options)
{
    this._sObject = options.sObject;
    this._iItemsStatic = undefined == options.iItemsStatic ? 0 : options.iItemsStatic;
    this._bItemsStaticOnly = undefined == options.bItemsStaticOnly ? 0 : options.bItemsStaticOnly;
    this._aHtmlIds = undefined == options.aHtmlIds ? {} : options.aHtmlIds;

    this._sKeyWidth = 'bx-mma-width';

    this._sClassReady = 'bx-mma-ready';
    this._sClassItem = '.bx-menu-item';
    this._sClassItemMore = this._sClassItem + '.bx-menu-item-more-auto';
    this._sClassItemMoreSubmenu = '.bx-menu-submenu-more-auto';
    this._sClassItemStatic = 'bx-menu-item-static';

    this._bStaticMode = this._bItemsStaticOnly && this._iItemsStatic > 0;
    this._bTrackContainerResize = false;
}

BxDolMenuMoreAuto.prototype.init = function() {
    var $this = this;

    var oMenu = $('#' + this._aHtmlIds['main']);
    if(!oMenu.length)
        return;

    //--- Initialize ---//
    var oItemMore = oMenu.find(this._sClassItemMore);
    var oItemMoreSubmenu = oItemMore.find(this._sClassItemMoreSubmenu);

    for(var i = 0; i < this._iItemsStatic; i++)
        oItemMore.prevAll(this._sClassItem + ':not(.' + this._sClassItemStatic + '):last').addClass(this._sClassItemStatic);

    var iMenu = 0;
    oMenu.children(this._sClassItem + ':visible').each(function() {
        iMenu += $this._getWidth($(this));
    });

    var iParent = oMenu.parent().width();
    var iItemMore = oItemMore.outerWidth(true);

    oMenu.css('overflow', 'visible');

    if(iMenu >= iParent || this._bStaticMode)
        this._moveToSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore);
    else
        oMenu.parents('.bx-menu-more-auto-wrapper:first').css('overflow', 'visible');

    //--- Add event handlers ---//
    if(!this._bStaticMode) {
        $(window).on('resize', function() {
           $this.update();
        });

        if(this._bTrackContainerResize)
            setTimeout(function () {
                oMenu.find(this._sClassItem).on('resize', function() {
                    $this.update(true);
                });
            }, 200);
    }
};

BxDolMenuMoreAuto.prototype.update = function(bForceCalculate)
{
    if(this._bStaticMode)
        return;

    var $this = this;
    var oMenu = $('#' + this._aHtmlIds['main']);
    var oItemMore = oMenu.find(this._sClassItemMore);
    var oItemMoreSubmenu = oItemMore.find(this._sClassItemMoreSubmenu);

    var iMenu = 0;
    oMenu.children(this._sClassItem + ':visible').each(function() {
        iMenu += $this._getWidth($(this), bForceCalculate);
    });

    var iParent = oMenu.parent().width();
    var iItemMore = oItemMore.outerWidth(true);

    if(iMenu > iParent)
        this._moveToSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore);
    if(iMenu < iParent)
        this._moveFromSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iMenu);
};

BxDolMenuMoreAuto.prototype.more = function(oElement)
{
    var oElement = $(oElement);

    oElement.parents('li:first').find('#' + this._aHtmlIds['more_auto_popup']).dolPopup({
        pointer: {
            el: oElement
        },
        moveToDocRoot: false,
        cssClass: 'bx-popup-menu'
    });
}

BxDolMenuMoreAuto.prototype._moveToSubmenu = function(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore)
{
    var $this = this;

    var bRelocateOthers = false;
    var iWidthTotal = iItemMore;
    var oSubmenuItemFirst = oItemMoreSubmenu.children(this._sClassItem + ':first');

    oMenu.children(this._sClassItem + ':not(' + this._sClassItemMore + ')').each(function() {
        var oItem = $(this);
        var iItem = $this._getWidth(oItem);
        if(($this._bStaticMode || bRelocateOthers || iWidthTotal + iItem > iParent) && !oItem.hasClass($this._sClassItemStatic)) {
            oItem.addClass('bx-def-color-bg-hl-hover');
            oItem.find('a').each(function() {
                if($(this).hasClass('bx-btn'))
                    $(this).removeClass('bx-btn').addClass('!bx-btn');

                if($(this).hasClass('bx-btn-height'))
                    $(this).removeClass('bx-btn-height').addClass('!bx-btn-height');
            });

            if(!oSubmenuItemFirst.length)
                oItemMoreSubmenu.append(oItem.detach());
            else
                oSubmenuItemFirst.before(oItem.detach());
            bRelocateOthers = true;
            return;
        }

        iWidthTotal += iItem;
    });

    if(oItemMoreSubmenu.find('li').length)
        oItemMore.show(function() {
            oMenu.addClass($this._sClassReady).parents('.bx-menu-more-auto-wrapper:first').css('overflow', 'visible');
        });
};

BxDolMenuMoreAuto.prototype._moveFromSubmenu = function(oMenu, oItemMore, oItemMoreSubmenu, iParent, iMenu)
{
    var $this = this;

    var bStopRelocation = false;
    var iWidthTotal = iMenu;
    oItemMoreSubmenu.children(this._sClassItem).each(function() {
        if(bStopRelocation)
            return;

        var oItem = $(this);
        var iItem = $this._getWidth(oItem);
        if(iWidthTotal + iItem > iParent) {
            bStopRelocation = true;
            return;
        }

        oItem.removeClass('bx-def-color-bg-hl-hover');
        oItem.find('a').each(function() {
            if($(this).hasClass('!bx-btn'))
                $(this).removeClass('!bx-btn').addClass('bx-btn');

            if($(this).hasClass('!bx-btn-height'))
                $(this).removeClass('!bx-btn-height').addClass('bx-btn-height');
        });

        oItemMore.before(oItem.detach());
        iWidthTotal += iItem;
    });

    if(!oItemMoreSubmenu.find('li').length)
        oItemMore.hide();
};

BxDolMenuMoreAuto.prototype._getWidth = function(oItem, bForceCalculate)
{
    var iItem = parseInt(oItem.attr(this._sKeyWidth));
    if(!bForceCalculate && iItem)
        return iItem;

    iItem = oItem.outerWidth(true);
    if(iItem)
        oItem.attr(this._sKeyWidth, iItem);

    return iItem;
}
/** @} */
<commit_msg>Ticket #3558<commit_after>/**
 * Copyright (c) UNA, Inc - https://una.io
 * MIT License - https://opensource.org/licenses/MIT
 *
 * @defgroup    UnaCore UNA Core
 * @{
 */

function BxDolMenuMoreAuto(options)
{
    this._sObject = options.sObject;
    this._iItemsStatic = undefined == options.iItemsStatic ? 0 : options.iItemsStatic;
    this._bItemsStaticOnly = undefined == options.bItemsStaticOnly ? 0 : options.bItemsStaticOnly;
    this._aHtmlIds = undefined == options.aHtmlIds ? {} : options.aHtmlIds;

    this._sKeyWidth = 'bx-mma-width';

    this._sClassReady = 'bx-mma-ready';
    this._sClassItem = '.bx-menu-item';
    this._sClassItemMore = this._sClassItem + '.bx-menu-item-more-auto';
    this._sClassItemMoreSubmenu = '.bx-menu-submenu-more-auto';
    this._sClassItemStatic = 'bx-menu-item-static';

    this._bStaticMode = this._bItemsStaticOnly && this._iItemsStatic > 0;
    this._bTrackContainerResize = false;
}

BxDolMenuMoreAuto.prototype.init = function() {
    var $this = this;

    var oMenu = $('#' + this._aHtmlIds['main']);
    if(!oMenu.length)
        return;

    //--- Initialize ---//
    var oItemMore = oMenu.find(this._sClassItemMore);
    var oItemMoreSubmenu = oItemMore.find(this._sClassItemMoreSubmenu);

    for(var i = 0; i < this._iItemsStatic; i++)
        oItemMore.prevAll(this._sClassItem + ':not(.' + this._sClassItemStatic + '):last').addClass(this._sClassItemStatic);

    var iMenu = 0;
    oMenu.children(this._sClassItem + ':visible').each(function() {
        iMenu += $this._getWidth($(this));
    });

    var iParent = oMenu.parent().width();
    var iItemMore = $this._getWidth(oItemMore);

    oMenu.css('overflow', 'visible');

    if(iMenu >= iParent || this._bStaticMode)
        this._moveToSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore);
    else
        oMenu.parents('.bx-menu-more-auto-wrapper:first').css('overflow', 'visible');

    //--- Add event handlers ---//
    if(!this._bStaticMode) {
        $(window).on('resize', function() {
           $this.update();
        });

        if(this._bTrackContainerResize)
            setTimeout(function () {
                oMenu.find(this._sClassItem).on('resize', function() {
                    $this.update(true);
                });
            }, 200);
    }
};

BxDolMenuMoreAuto.prototype.update = function(bForceCalculate)
{
    if(this._bStaticMode)
        return;

    var $this = this;
    var oMenu = $('#' + this._aHtmlIds['main']);
    var oItemMore = oMenu.find(this._sClassItemMore);
    var oItemMoreSubmenu = oItemMore.find(this._sClassItemMoreSubmenu);

    var iMenu = 0;
    oMenu.children(this._sClassItem + ':visible').each(function() {
        iMenu += $this._getWidth($(this), bForceCalculate);
    });

    var iParent = oMenu.parent().width();
    var iItemMore = oItemMore.outerWidth(true);

    if(iMenu > iParent)
        this._moveToSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore);
    if(iMenu < iParent)
        this._moveFromSubmenu(oMenu, oItemMore, oItemMoreSubmenu, iParent, iMenu);
};

BxDolMenuMoreAuto.prototype.more = function(oElement)
{
    var oElement = $(oElement);

    oElement.parents('li:first').find('#' + this._aHtmlIds['more_auto_popup']).dolPopup({
        pointer: {
            el: oElement
        },
        moveToDocRoot: false,
        cssClass: 'bx-popup-menu'
    });
}

BxDolMenuMoreAuto.prototype._moveToSubmenu = function(oMenu, oItemMore, oItemMoreSubmenu, iParent, iItemMore)
{
    var $this = this;

    var bRelocateOthers = false;
    var iWidthTotal = iItemMore;
    var oSubmenuItemFirst = oItemMoreSubmenu.children(this._sClassItem + ':first');

    oMenu.children(this._sClassItem + ':not(' + this._sClassItemMore + ')').each(function() {
        var oItem = $(this);
        var iItem = $this._getWidth(oItem);
        if(($this._bStaticMode || bRelocateOthers || iWidthTotal + iItem > iParent) && !oItem.hasClass($this._sClassItemStatic)) {
            oItem.addClass('bx-def-color-bg-hl-hover');
            oItem.find('a').each(function() {
                if($(this).hasClass('bx-btn'))
                    $(this).removeClass('bx-btn').addClass('!bx-btn');

                if($(this).hasClass('bx-btn-height'))
                    $(this).removeClass('bx-btn-height').addClass('!bx-btn-height');
            });

            if(!oSubmenuItemFirst.length)
                oItemMoreSubmenu.append(oItem.detach());
            else
                oSubmenuItemFirst.before(oItem.detach());
            bRelocateOthers = true;
            return;
        }

        iWidthTotal += iItem;
    });

    if(oItemMoreSubmenu.find('li').length)
        oItemMore.show(function() {
            oMenu.addClass($this._sClassReady).parents('.bx-menu-more-auto-wrapper:first').css('overflow', 'visible');
        });
};

BxDolMenuMoreAuto.prototype._moveFromSubmenu = function(oMenu, oItemMore, oItemMoreSubmenu, iParent, iMenu)
{
    var $this = this;

    var bStopRelocation = false;
    var iWidthTotal = iMenu;
    oItemMoreSubmenu.children(this._sClassItem).each(function() {
        if(bStopRelocation)
            return;

        var oItem = $(this);
        var iItem = $this._getWidth(oItem);
        if(iWidthTotal + iItem > iParent) {
            bStopRelocation = true;
            return;
        }

        oItem.removeClass('bx-def-color-bg-hl-hover');
        oItem.find('a').each(function() {
            if($(this).hasClass('!bx-btn'))
                $(this).removeClass('!bx-btn').addClass('bx-btn');

            if($(this).hasClass('!bx-btn-height'))
                $(this).removeClass('!bx-btn-height').addClass('bx-btn-height');
        });

        oItemMore.before(oItem.detach());
        iWidthTotal += iItem;
    });

    if(!oItemMoreSubmenu.find('li').length)
        oItemMore.hide();
};

BxDolMenuMoreAuto.prototype._getWidth = function(oItem, bForceCalculate)
{
    var iItem = parseInt(oItem.attr(this._sKeyWidth));
    if(!bForceCalculate && iItem)
        return iItem;

    iItem = oItem.outerWidth(true);
    if(iItem)
        oItem.attr(this._sKeyWidth, iItem);

    return iItem;
}
/** @} */
<|endoftext|>


<commit_before><script>
  var host = "drissamri.be";
  if ((host === window.location.host) && (window.location.protocol !== "https:")) {
    window.location.protocol = "https";
  }
</script>
<commit_msg>HTTP to HTTPS redirect JS<commit_after>  var host = "drissamri.be";
  if ((host === window.location.host) && (window.location.protocol !== "https:")) {
    window.location.protocol = "https";
  }
<|endoftext|>


<commit_before>JAR.register({
    MID: 'jar.lang.MixIn',
    deps: ['System', '.Class', '.Object', '.Array!check|derive', '.Function']
}, function(System, Class, Obj, Arr, Fn) {
    'use strict';

    var MixIn = Class('MixIn', {
        $: {
            construct: function(mixInName, toMix, allowedClasses, destructor) {
                this._$name = mixInName;
                this._$toMix = Obj.from(toMix);
                this._$allowedClasses = Arr.filter(System.isArray(allowedClasses) ? allowedClasses : [allowedClasses], Class.isClass);
                this._$destructor = destructor;
                this._$logger = System.getCustomLogger('MixIn "#<' + mixInName + '>"');
            },

            mixInto: function(receiver, mixInAnyObject) {
                var logger = this._$logger,
                    isReceiverAllowed = this._$isReceiverAllowed.bind(this, receiver),
                    toMix = this._$toMix,
                    allowedClasses = this._$allowedClasses,
                    destructor = this._$destructor,
                    objectToExtend;

                if (receiver) {

                    if (Arr.every(allowedClasses, isReceiverAllowed)) {
                        if (Class.isClass(receiver)) {
                            objectToExtend = receiver.prototype;
                            receiver.addDestructor(destructor);
                        }
                        else if (Class.isInstance(receiver)) {
                            objectToExtend = receiver;
                            receiver.Class.addDestructor(destructor, receiver);
                        }
                        else if (mixInAnyObject && System.isObject(receiver)) {
                            objectToExtend = receiver;
                        }

                        objectToExtend && Obj.extend(objectToExtend, toMix);
                    }
                    else {
                        logger('The given receiver "' + receiver + '" is not part of the allowed Classes!', 'warn');
                    }
                }
                else {
                    logger('There is no receiver given!', 'warn');
                }

                return receiver;
            },

            getName: function() {
                return this._$name;
            }
        },

        _$: {
            name: '',

            logger: null,

            toMix: null,

            allowedClasses: null,

            destructor: null,

            isReceiverAllowed: Fn.from(function(receiver, allowedClass) {
                return receiver === allowedClass || allowedClass.isSuperClassOf(receiver) || System.isA(receiver, allowedClass);
            })
        }
    });

    /**
     * Define a mixin-method that mixes the MixIn into the Class
     * It is available for every Class created with jar.lang.Class()
     * as soon as this module is loaded
     */
    function mixin(mixIn) {
        if (System.isA(mixIn, MixIn)) {
            mixIn.mixInto(this);
        }

        return this;
    }

    Class.addStatic('mixin', mixin);

    return MixIn;
});<commit_msg>Refactored MixIn-messages as predefined templates<commit_after>JAR.register({
    MID: 'jar.lang.MixIn',
    deps: ['System', '.Class', '.Object', '.Array!check|derive', '.Function!modargs']
}, function(System, Class, Obj, Arr, Fn) {
    'use strict';

    var RECEIVER_MISSING = 0,
        RECEIVER_NOT_ALLOWED = 1,
        mixinTemplates = [],
        MixIn;

    mixinTemplates[RECEIVER_MISSING] = 'There is no receiver given!';
    mixinTemplates[RECEIVER_NOT_ALLOWED] = 'The given receiver "{{receiver}}" is not part of the allowed Classes!';

    MixIn = Class('MixIn', {
        $: {
            construct: function(mixInName, toMix, allowedClasses, destructor) {
                this._$name = mixInName;
                this._$toMix = Obj.from(toMix);
                this._$allowedClasses = Arr.filter(System.isArray(allowedClasses) ? allowedClasses : [allowedClasses], Class.isClass);
                this._$destructor = destructor;
                this._$log = System.getCustomLog('MixIn "#<' + mixInName + '>"', mixinTemplates);
            },

            mixInto: function(receiver, mixInAnyObject) {
                var log = this._$log,
                    isReceiverAllowed = Fn.partial(this._$isReceiverAllowed, receiver),
                    toMix = this._$toMix,
                    allowedClasses = this._$allowedClasses,
                    destructor = this._$destructor,
                    objectToExtend;

                if (receiver) {

                    if (Arr.every(allowedClasses, isReceiverAllowed)) {
                        if (Class.isClass(receiver)) {
                            objectToExtend = receiver.prototype;
                            receiver.addDestructor(destructor);
                        }
                        else if (Class.isInstance(receiver)) {
                            objectToExtend = receiver;
                            receiver.Class.addDestructor(destructor, receiver);
                        }
                        else if (mixInAnyObject && System.isObject(receiver)) {
                            objectToExtend = receiver;
                        }

                        objectToExtend && Obj.extend(objectToExtend, toMix);
                    }
                    else {
                        log(RECEIVER_NOT_ALLOWED, 'warn', {
                            receiver: receiver
                        });
                    }
                }
                else {
                    log(RECEIVER_MISSING, 'warn');
                }

                return receiver;
            },

            getName: function() {
                return this._$name;
            }
        },

        _$: {
            name: '',

            log: null,

            toMix: null,

            allowedClasses: null,

            destructor: null,

            isReceiverAllowed: function(receiver, allowedClass) {
                return receiver === allowedClass || allowedClass.isSuperClassOf(receiver) || System.isA(receiver, allowedClass);
            }
        }
    });

    /**
     * Define a mixin-method that mixes the MixIn into the Class
     * It is available for every Class created with jar.lang.Class()
     * as soon as this module is loaded
     */
    function mixin(mixIn) {
        if (System.isA(mixIn, MixIn)) {
            mixIn.mixInto(this);
        }

        return this;
    }

    Class.addStatic('mixin', mixin);

    return MixIn;
});<|endoftext|>


<commit_before><commit_msg>Modify cleanDatabase in order to use events instead of iter and adds link<commit_after><|endoftext|>