<commit_before>'use strict';

const _ = require('lodash');

// Media state
//
// - user_hid:           user hid
// - album_hid:          album hid
// - reached_start:      true if no more pages exist above first loaded one
// - reached_end:        true if no more pages exist below last loaded one
// - prev_loading_start: time when current xhr request for the previous page is started
// - next_loading_start: time when current xhr request for the next page is started
//
let mediaState = {};

let $window = $(window);

let navbarHeight = $('.navbar').height();

// offset between navbar and the first media
const TOP_OFFSET = 50;

/////////////////////////////////////////////////////////////////////
// init on page load
//
N.wire.on('navigate.done:' + module.apiPath, function page_setup(data) {
  mediaState.user_hid           = data.params.user_hid;
  mediaState.album_id           = data.params.album_id;

  mediaState.reached_start      = !N.runtime.page_data.prev_media;
  mediaState.reached_end        = !N.runtime.page_data.next_media;

  mediaState.prev_loading_start = 0;
  mediaState.next_loading_start = 0;

  // disable automatic scroll to an anchor in the navigator
  data.no_scroll = true;

  let el;

  if (data.state && typeof data.state.media !== 'undefined' && typeof data.state.offset !== 'undefined') {
    el = $('#media' + data.state.media);

    if (el.length) {
      $window.scrollTop(el.offset().top - $('.navbar').height() - TOP_OFFSET + data.state.offset);
      return;
    }

  } else if (data.params.media_id) {
    el = $('#media' + data.params.media_id);

    if (el.length) {
      $window.scrollTop(el.offset().top - $('.navbar').height() - TOP_OFFSET);
      return;
    }
  }

  // If we're on the first page, scroll to the top;
  // otherwise, scroll to the first topic on that page
  //
  if (!mediaState.reached_start && $('#users-media-list').length) {
    $window.scrollTop($('#users-media-list').offset().top - navbarHeight);

  } else {
    $window.scrollTop(0);
  }
});

////////////////////////////////////////////////////////////////////////////////
// Uploader
//

let $dropZone;

N.wire.after('navigate.done:' + module.apiPath, function uploader_setup() {
  $dropZone = $('.user-album-upload');

  $('#user-album-upload__files').on('change', function () {
    let files = $(this).get(0).files;

    if (files.length > 0) {
      let params = {
        files,
        url: N.router.linkTo('users.media.upload', { album_id: mediaState.album_id }),
        config: 'users.uploader_config',
        uploaded: null
      };

      N.wire.emit('users.uploader:add', params)
        .then(() => {
          $('#users-media-list').prepend(
            $(N.runtime.render('users.album.list', { media: params.uploaded, user_hid: mediaState.user_hid }))
          );
          $('.user-album-root').removeClass('no-files');
        })
        .catch(err => N.wire.emit('error', err));
    }
  });
});

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  // Handles the event when user drag file to drag drop zone
  //
  N.wire.on(module.apiPath + ':dd_area', function user_album_dd(data) {
    let x0, y0, x1, y1, ex, ey;

    switch (data.event.type) {
      case 'dragenter':
        $dropZone.addClass('active');
        break;

      case 'dragleave':
        // 'dragleave' occurs when user move cursor over child HTML element
        // track this situation and don't remove 'active' class
        // http://stackoverflow.com/questions/10867506/
        x0 = $dropZone.offset().left;
        y0 = $dropZone.offset().top;
        x1 = x0 + $dropZone.outerWidth();
        y1 = y0 + $dropZone.outerHeight();
        ex = data.event.originalEvent.pageX;
        ey = data.event.originalEvent.pageY;

        if (ex > x1 || ex < x0 || ey > y1 || ey < y0) {
          $dropZone.removeClass('active');
        }
        break;

      case 'drop':
        $dropZone.removeClass('active');

        if (data.files && data.files.length) {
          let params = {
            files: data.files,
            url: N.router.linkTo('users.media.upload', { album_id: mediaState.album_id }),
            config: 'users.uploader_config',
            uploaded: null
          };

          return N.wire.emit('users.uploader:add', params)
            .then(() => {
              $('#users-media-list').prepend(
                $(N.runtime.render('users.album.list', { media: params.uploaded, user_hid: mediaState.user_hid }))
              );
              $('.user-album-root').removeClass('no-files');
            });
        }
        break;

      default:
    }
  });
});

////////////////////////////////////////////////////////////////////////////////
// Create medialink button handler
//

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  N.wire.on(module.apiPath + ':add_medialink', function add_medialink(data) {
    let params = {
      album_id: mediaState.album_id,
      providers: data.$this.data('providers'),
      media_url: null
    };

    return Promise.resolve()
      .then(() => N.wire.emit('users.album.add_medialink', params))
      .then(() => N.io.rpc('users.media.add_medialink', { album_id: params.album_id, media_url: params.media_url }))
      .then(() => {
        $('#users-media-list').prepend(
          $(N.runtime.render('users.album.list', { media: [ params.media ], user_hid: mediaState.user_hid }))
        );
        $('.user-album-root').removeClass('no-files');
      });
  });
});

/////////////////////////////////////////////////////////////////////
// Change URL when user scrolls the page
//
let locationScrollHandler = null;

N.wire.on('navigate.done:' + module.apiPath, function location_updater_init() {
  locationScrollHandler = _.debounce(function update_location_on_scroll() {
    let media          = document.getElementById('users-media-list').getElementsByClassName('thumb'),
        mediaThreshold = $window.scrollTop() + navbarHeight + TOP_OFFSET,
        currentIdx;

    // Get offset of the first media in the viewport
    //
    currentIdx = _.sortedIndexBy(media, null, medium => {
      if (!medium) { return mediaThreshold; }
      return medium.offsetTop + $(medium).height();
    });

    if (currentIdx === 0 && media[currentIdx].offsetTop > mediaThreshold) {
      currentIdx--;
    }

    if (currentIdx >= media.length) { currentIdx = media.length - 1; }

    let href = null;
    let state = null;

    if (currentIdx >= 0 && media.length) {
      state = {
        media:  $(media[currentIdx]).data('media-id'),
        offset: mediaThreshold - media[currentIdx].offsetTop
      };
    }

    /* eslint-disable no-undefined */
    href = N.router.linkTo('users.album', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: currentIdx >= 0 ? $(media[currentIdx]).data('media-id') : undefined
    });

    // all photos, 1st page    - noindex, nofollow
    // all photos, other pages - noindex, nofollow
    // album, 1st page         - index,   follow
    // album, other pages      - noindex, follow
    //
    let index  = mediaState.album_id && currentIdx < 0;
    let follow = mediaState.album_id;
    let tag    = $('meta[name="robots"]');

    if (index && follow) {
      tag.remove();
    } else {
      if (!tag.length) $('head').append(tag = $('<meta name="robots">'));

      tag.attr('content', (index  ? 'index'  : 'noindex') + ',' +
                          (follow ? 'follow' : 'nofollow'));
    }

    N.wire.emit('navigate.replace', { href, state })
          .catch(err => N.wire.emit('error', err));

  }, 500);

  // avoid executing it on first tick because of initial scrollTop()
  setTimeout(function () {
    $window.on('scroll', locationScrollHandler);
    $window.on('resize', locationScrollHandler);
  }, 1);
});

N.wire.on('navigate.exit:' + module.apiPath, function location_updater_teardown() {
  if (!locationScrollHandler) return;
  locationScrollHandler.cancel();
  $window.off('scroll', locationScrollHandler);
  $window.off('resize', locationScrollHandler);
  locationScrollHandler = null;
});

///////////////////////////////////////////////////////////////////////////
// Whenever we are close to beginning/end of media list, check if we can
// load more pages from the server
//
let prefetchScrollHandler = null;

N.wire.on('navigate.done:' + module.apiPath, function prefetcher_init() {
  // an amount of time between successful xhr requests and failed xhr requests respectively
  //
  // For example, suppose user continuously scrolls. If server is up, each
  // subsequent request will be sent each 100 ms. If server goes down, the
  // interval between request initiations goes up to 2000 ms.
  //
  const LOAD_INTERVAL    = 100;
  const LOAD_AFTER_ERROR = 2000;

  // an amount of media files we try to load when user scrolls
  // to the end of the page
  const LOAD_MEDIA_ALL   = 30;
  const LOAD_MEDIA_ALBUM = 100;

  function load_prev_page() {
    if (mediaState.reached_start) return;

    let now = Date.now();

    // `prev_loading_start` is the last request start time, which is reset to 0 on success
    //
    // Thus, successful requests can restart immediately, but failed ones
    // will have to wait `LOAD_AFTER_ERROR` ms.
    //
    if (Math.abs(mediaState.prev_loading_start - now) < LOAD_AFTER_ERROR) return;

    mediaState.prev_loading_start = now;

    let media = document.getElementById('users-media-list').getElementsByClassName('thumb');
    let first_offset = media[0].offsetTop;
    let i;

    for (i = 1; i < media.length; i++) {
      if (media[i].offsetTop !== first_offset) break;
    }

    let columns    = i;
    let load_count = mediaState.album_id ? LOAD_MEDIA_ALBUM : LOAD_MEDIA_ALL;

    // Make sure we will have filled lines after load (if possible)
    //
    load_count -= (load_count + media.length) % columns;

    N.io.rpc('users.album.list', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: $('#users-media-list .thumb:first').data('media-id'),
      before:   load_count,
      after:    0
    }).then(function (res) {
      // Stop debouncer to avoid progress bar jump after page load
      if ($window.scrollTop() <= 0) {
        prefetchScrollHandler.cancel();
      }

      if (!res.media) return;

      if (!res.prev_media) {
        mediaState.reached_start = true;
      }

      if (res.media.length === 0) return;

      let old_height = $('#users-media-list').height();

      // render & inject media list
      let $result = $(N.runtime.render('users.album.list', res));
      $('#users-media-list').prepend($result);

      // update scroll so it would point at the same spot as before
      $window.scrollTop($window.scrollTop() + $('#users-media-list').height() - old_height);

      // update prev/next metadata
      $('link[rel="prev"]').remove();

      if (res.head.prev) {
        let link = $('<link rel="prev">');

        link.attr('href', res.head.prev);
        $('head').append(link);
      }

      mediaState.prev_loading_start = 0;
    }).catch(err => {
      N.wire.emit('error', err);
    });
  }

  function load_next_page() {
    if (mediaState.reached_end) return;

    let now = Date.now();

    // `next_loading_start` is the last request start time, which is reset to 0 on success
    //
    // Thus, successful requests can restart immediately, but failed ones
    // will have to wait `LOAD_AFTER_ERROR` ms.
    //
    if (Math.abs(mediaState.next_loading_start - now) < LOAD_AFTER_ERROR) return;

    mediaState.next_loading_start = now;

    let media = document.getElementById('users-media-list').getElementsByClassName('thumb');
    let first_offset = media[0].offsetTop;
    let i;

    for (i = 1; i < media.length; i++) {
      if (media[i].offsetTop !== first_offset) break;
    }

    let columns    = i;
    let load_count = mediaState.album_id ? LOAD_MEDIA_ALBUM : LOAD_MEDIA_ALL;

    // Make sure we will have filled lines after load (if possible)
    //
    load_count -= (load_count + media.length) % columns;

    N.io.rpc('users.album.list', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: $('#users-media-list .thumb:last').data('media-id'),
      before:   0,
      after:    load_count
    }).then(function (res) {
      let scrollHeight = (document.documentElement && document.documentElement.scrollHeight) ||
                         document.body.scrollHeight;

      // Stop debouncer to avoid progress bar jump after page load
      if ($window.height() + $window.scrollTop() >= scrollHeight) {
        prefetchScrollHandler.cancel();
      }

      if (!res.media) return;

      if (!res.next_media) {
        mediaState.reached_end = true;
      }

      if (res.media.length === 0) return;

      // render & inject media list
      let $result = $(N.runtime.render('users.album.list', res));
      $('#users-media-list').append($result);

      // update next/next metadata
      $('link[rel="next"]').remove();

      if (res.head.next) {
        let link = $('<link rel="next">');

        link.attr('href', res.head.next);
        $('head').append(link);
      }

      mediaState.next_loading_start = 0;
    }).catch(err => {
      N.wire.emit('error', err);
    });
  }

  // If we're browsing one of the first/last 3 media rows, load more pages from
  // the server in that direction.
  //
  prefetchScrollHandler = _.debounce(function prefetch_on_scroll() {
    let media         = document.getElementById('users-media-list').getElementsByClassName('thumb'),
        viewportStart = $window.scrollTop() + navbarHeight,
        viewportEnd   = $window.scrollTop() + $window.height();

    if (media.length <= 1 || media[media.length - 1].offsetTop < viewportEnd) {
      load_next_page();
    }

    if (media.length <= 1 || media[0].offsetTop > viewportStart) {
      load_prev_page();
    }
  }, LOAD_INTERVAL, { maxWait: LOAD_INTERVAL });

  // avoid executing it on first tick because of initial scrollTop()
  setTimeout(function () {
    $window.on('scroll', prefetchScrollHandler);
    $window.on('resize', prefetchScrollHandler);
  }, 1);
});

N.wire.on('navigate.exit:' + module.apiPath, function prefetcher_teardown() {
  if (!prefetchScrollHandler) return;
  prefetchScrollHandler.cancel();
  $window.off('scroll', prefetchScrollHandler);
  $window.off('resize', prefetchScrollHandler);
  prefetchScrollHandler = null;
});
<commit_msg>fixed reload after add video to albums<commit_after>'use strict';

const _ = require('lodash');

// Media state
//
// - user_hid:           user hid
// - album_hid:          album hid
// - reached_start:      true if no more pages exist above first loaded one
// - reached_end:        true if no more pages exist below last loaded one
// - prev_loading_start: time when current xhr request for the previous page is started
// - next_loading_start: time when current xhr request for the next page is started
//
let mediaState = {};

let $window = $(window);

let navbarHeight = $('.navbar').height();

// offset between navbar and the first media
const TOP_OFFSET = 50;

/////////////////////////////////////////////////////////////////////
// init on page load
//
N.wire.on('navigate.done:' + module.apiPath, function page_setup(data) {
  mediaState.user_hid           = data.params.user_hid;
  mediaState.album_id           = data.params.album_id;

  mediaState.reached_start      = !N.runtime.page_data.prev_media;
  mediaState.reached_end        = !N.runtime.page_data.next_media;

  mediaState.prev_loading_start = 0;
  mediaState.next_loading_start = 0;

  // disable automatic scroll to an anchor in the navigator
  data.no_scroll = true;

  let el;

  if (data.state && typeof data.state.media !== 'undefined' && typeof data.state.offset !== 'undefined') {
    el = $('#media' + data.state.media);

    if (el.length) {
      $window.scrollTop(el.offset().top - $('.navbar').height() - TOP_OFFSET + data.state.offset);
      return;
    }

  } else if (data.params.media_id) {
    el = $('#media' + data.params.media_id);

    if (el.length) {
      $window.scrollTop(el.offset().top - $('.navbar').height() - TOP_OFFSET);
      return;
    }
  }

  // If we're on the first page, scroll to the top;
  // otherwise, scroll to the first topic on that page
  //
  if (!mediaState.reached_start && $('#users-media-list').length) {
    $window.scrollTop($('#users-media-list').offset().top - navbarHeight);

  } else {
    $window.scrollTop(0);
  }
});

////////////////////////////////////////////////////////////////////////////////
// Uploader
//

let $dropZone;

N.wire.after('navigate.done:' + module.apiPath, function uploader_setup() {
  $dropZone = $('.user-album-upload');

  $('#user-album-upload__files').on('change', function () {
    let files = $(this).get(0).files;

    if (files.length > 0) {
      let params = {
        files,
        url: N.router.linkTo('users.media.upload', { album_id: mediaState.album_id }),
        config: 'users.uploader_config',
        uploaded: null
      };

      N.wire.emit('users.uploader:add', params)
        .then(() => {
          $('#users-media-list').prepend(
            $(N.runtime.render('users.album.list', { media: params.uploaded, user_hid: mediaState.user_hid }))
          );
          $('.user-album-root').removeClass('no-files');
        })
        .catch(err => N.wire.emit('error', err));
    }
  });
});

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  // Handles the event when user drag file to drag drop zone
  //
  N.wire.on(module.apiPath + ':dd_area', function user_album_dd(data) {
    let x0, y0, x1, y1, ex, ey;

    switch (data.event.type) {
      case 'dragenter':
        $dropZone.addClass('active');
        break;

      case 'dragleave':
        // 'dragleave' occurs when user move cursor over child HTML element
        // track this situation and don't remove 'active' class
        // http://stackoverflow.com/questions/10867506/
        x0 = $dropZone.offset().left;
        y0 = $dropZone.offset().top;
        x1 = x0 + $dropZone.outerWidth();
        y1 = y0 + $dropZone.outerHeight();
        ex = data.event.originalEvent.pageX;
        ey = data.event.originalEvent.pageY;

        if (ex > x1 || ex < x0 || ey > y1 || ey < y0) {
          $dropZone.removeClass('active');
        }
        break;

      case 'drop':
        $dropZone.removeClass('active');

        if (data.files && data.files.length) {
          let params = {
            files: data.files,
            url: N.router.linkTo('users.media.upload', { album_id: mediaState.album_id }),
            config: 'users.uploader_config',
            uploaded: null
          };

          return N.wire.emit('users.uploader:add', params)
            .then(() => {
              $('#users-media-list').prepend(
                $(N.runtime.render('users.album.list', { media: params.uploaded, user_hid: mediaState.user_hid }))
              );
              $('.user-album-root').removeClass('no-files');
            });
        }
        break;

      default:
    }
  });
});

////////////////////////////////////////////////////////////////////////////////
// Create medialink button handler
//

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  N.wire.on(module.apiPath + ':add_medialink', function add_medialink(data) {
    let params = {
      album_id: mediaState.album_id,
      providers: data.$this.data('providers'),
      media_url: null
    };

    return Promise.resolve()
      .then(() => N.wire.emit('users.album.add_medialink', params))
      .then(() => N.io.rpc('users.media.add_medialink', { album_id: params.album_id, media_url: params.media_url }))
      .then(res => {
        $('#users-media-list').prepend(
          $(N.runtime.render('users.album.list', { media: [ res.media ], user_hid: mediaState.user_hid }))
        );
        $('.user-album-root').removeClass('no-files');
      });
  });
});

/////////////////////////////////////////////////////////////////////
// Change URL when user scrolls the page
//
let locationScrollHandler = null;

N.wire.on('navigate.done:' + module.apiPath, function location_updater_init() {
  locationScrollHandler = _.debounce(function update_location_on_scroll() {
    let media          = document.getElementById('users-media-list').getElementsByClassName('thumb'),
        mediaThreshold = $window.scrollTop() + navbarHeight + TOP_OFFSET,
        currentIdx;

    // Get offset of the first media in the viewport
    //
    currentIdx = _.sortedIndexBy(media, null, medium => {
      if (!medium) { return mediaThreshold; }
      return medium.offsetTop + $(medium).height();
    });

    if (currentIdx === 0 && media[currentIdx].offsetTop > mediaThreshold) {
      currentIdx--;
    }

    if (currentIdx >= media.length) { currentIdx = media.length - 1; }

    let href = null;
    let state = null;

    if (currentIdx >= 0 && media.length) {
      state = {
        media:  $(media[currentIdx]).data('media-id'),
        offset: mediaThreshold - media[currentIdx].offsetTop
      };
    }

    /* eslint-disable no-undefined */
    href = N.router.linkTo('users.album', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: currentIdx >= 0 ? $(media[currentIdx]).data('media-id') : undefined
    });

    // all photos, 1st page    - noindex, nofollow
    // all photos, other pages - noindex, nofollow
    // album, 1st page         - index,   follow
    // album, other pages      - noindex, follow
    //
    let index  = mediaState.album_id && currentIdx < 0;
    let follow = mediaState.album_id;
    let tag    = $('meta[name="robots"]');

    if (index && follow) {
      tag.remove();
    } else {
      if (!tag.length) $('head').append(tag = $('<meta name="robots">'));

      tag.attr('content', (index  ? 'index'  : 'noindex') + ',' +
                          (follow ? 'follow' : 'nofollow'));
    }

    N.wire.emit('navigate.replace', { href, state })
          .catch(err => N.wire.emit('error', err));

  }, 500);

  // avoid executing it on first tick because of initial scrollTop()
  setTimeout(function () {
    $window.on('scroll', locationScrollHandler);
    $window.on('resize', locationScrollHandler);
  }, 1);
});

N.wire.on('navigate.exit:' + module.apiPath, function location_updater_teardown() {
  if (!locationScrollHandler) return;
  locationScrollHandler.cancel();
  $window.off('scroll', locationScrollHandler);
  $window.off('resize', locationScrollHandler);
  locationScrollHandler = null;
});

///////////////////////////////////////////////////////////////////////////
// Whenever we are close to beginning/end of media list, check if we can
// load more pages from the server
//
let prefetchScrollHandler = null;

N.wire.on('navigate.done:' + module.apiPath, function prefetcher_init() {
  // an amount of time between successful xhr requests and failed xhr requests respectively
  //
  // For example, suppose user continuously scrolls. If server is up, each
  // subsequent request will be sent each 100 ms. If server goes down, the
  // interval between request initiations goes up to 2000 ms.
  //
  const LOAD_INTERVAL    = 100;
  const LOAD_AFTER_ERROR = 2000;

  // an amount of media files we try to load when user scrolls
  // to the end of the page
  const LOAD_MEDIA_ALL   = 30;
  const LOAD_MEDIA_ALBUM = 100;

  function load_prev_page() {
    if (mediaState.reached_start) return;

    let now = Date.now();

    // `prev_loading_start` is the last request start time, which is reset to 0 on success
    //
    // Thus, successful requests can restart immediately, but failed ones
    // will have to wait `LOAD_AFTER_ERROR` ms.
    //
    if (Math.abs(mediaState.prev_loading_start - now) < LOAD_AFTER_ERROR) return;

    mediaState.prev_loading_start = now;

    let media = document.getElementById('users-media-list').getElementsByClassName('thumb');
    let first_offset = media[0].offsetTop;
    let i;

    for (i = 1; i < media.length; i++) {
      if (media[i].offsetTop !== first_offset) break;
    }

    let columns    = i;
    let load_count = mediaState.album_id ? LOAD_MEDIA_ALBUM : LOAD_MEDIA_ALL;

    // Make sure we will have filled lines after load (if possible)
    //
    load_count -= (load_count + media.length) % columns;

    N.io.rpc('users.album.list', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: $('#users-media-list .thumb:first').data('media-id'),
      before:   load_count,
      after:    0
    }).then(function (res) {
      // Stop debouncer to avoid progress bar jump after page load
      if ($window.scrollTop() <= 0) {
        prefetchScrollHandler.cancel();
      }

      if (!res.media) return;

      if (!res.prev_media) {
        mediaState.reached_start = true;
      }

      if (res.media.length === 0) return;

      let old_height = $('#users-media-list').height();

      // render & inject media list
      let $result = $(N.runtime.render('users.album.list', res));
      $('#users-media-list').prepend($result);

      // update scroll so it would point at the same spot as before
      $window.scrollTop($window.scrollTop() + $('#users-media-list').height() - old_height);

      // update prev/next metadata
      $('link[rel="prev"]').remove();

      if (res.head.prev) {
        let link = $('<link rel="prev">');

        link.attr('href', res.head.prev);
        $('head').append(link);
      }

      mediaState.prev_loading_start = 0;
    }).catch(err => {
      N.wire.emit('error', err);
    });
  }

  function load_next_page() {
    if (mediaState.reached_end) return;

    let now = Date.now();

    // `next_loading_start` is the last request start time, which is reset to 0 on success
    //
    // Thus, successful requests can restart immediately, but failed ones
    // will have to wait `LOAD_AFTER_ERROR` ms.
    //
    if (Math.abs(mediaState.next_loading_start - now) < LOAD_AFTER_ERROR) return;

    mediaState.next_loading_start = now;

    let media = document.getElementById('users-media-list').getElementsByClassName('thumb');
    let first_offset = media[0].offsetTop;
    let i;

    for (i = 1; i < media.length; i++) {
      if (media[i].offsetTop !== first_offset) break;
    }

    let columns    = i;
    let load_count = mediaState.album_id ? LOAD_MEDIA_ALBUM : LOAD_MEDIA_ALL;

    // Make sure we will have filled lines after load (if possible)
    //
    load_count -= (load_count + media.length) % columns;

    N.io.rpc('users.album.list', {
      user_hid: mediaState.user_hid,
      album_id: mediaState.album_id,
      media_id: $('#users-media-list .thumb:last').data('media-id'),
      before:   0,
      after:    load_count
    }).then(function (res) {
      let scrollHeight = (document.documentElement && document.documentElement.scrollHeight) ||
                         document.body.scrollHeight;

      // Stop debouncer to avoid progress bar jump after page load
      if ($window.height() + $window.scrollTop() >= scrollHeight) {
        prefetchScrollHandler.cancel();
      }

      if (!res.media) return;

      if (!res.next_media) {
        mediaState.reached_end = true;
      }

      if (res.media.length === 0) return;

      // render & inject media list
      let $result = $(N.runtime.render('users.album.list', res));
      $('#users-media-list').append($result);

      // update next/next metadata
      $('link[rel="next"]').remove();

      if (res.head.next) {
        let link = $('<link rel="next">');

        link.attr('href', res.head.next);
        $('head').append(link);
      }

      mediaState.next_loading_start = 0;
    }).catch(err => {
      N.wire.emit('error', err);
    });
  }

  // If we're browsing one of the first/last 3 media rows, load more pages from
  // the server in that direction.
  //
  prefetchScrollHandler = _.debounce(function prefetch_on_scroll() {
    let media         = document.getElementById('users-media-list').getElementsByClassName('thumb'),
        viewportStart = $window.scrollTop() + navbarHeight,
        viewportEnd   = $window.scrollTop() + $window.height();

    if (media.length <= 1 || media[media.length - 1].offsetTop < viewportEnd) {
      load_next_page();
    }

    if (media.length <= 1 || media[0].offsetTop > viewportStart) {
      load_prev_page();
    }
  }, LOAD_INTERVAL, { maxWait: LOAD_INTERVAL });

  // avoid executing it on first tick because of initial scrollTop()
  setTimeout(function () {
    $window.on('scroll', prefetchScrollHandler);
    $window.on('resize', prefetchScrollHandler);
  }, 1);
});

N.wire.on('navigate.exit:' + module.apiPath, function prefetcher_teardown() {
  if (!prefetchScrollHandler) return;
  prefetchScrollHandler.cancel();
  $window.off('scroll', prefetchScrollHandler);
  $window.off('resize', prefetchScrollHandler);
  prefetchScrollHandler = null;
});
<|endoftext|>


<commit_before>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 1000)
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits / total
		if (usage > 0.01) {
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (!hot.length)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<commit_msg>do not remove/add same class<commit_after>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 1000)
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits / total
		if (usage > 0.01) {
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (!hot.length)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0 && this._class !== cls) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<|endoftext|>


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

jest.dontMock('../../mixins/GetSetMixin');
jest.dontMock('../MesosSummaryStore');
jest.dontMock('./fixtures/MockStates.json');
jest.dontMock('./fixtures/MockAppMetadata');
jest.dontMock('./fixtures/MockParsedAppMetadata');
jest.dontMock('../../utils/MesosSummaryUtil');
jest.dontMock('../../utils/RequestUtil');
jest.dontMock('../../utils/StringUtil');
jest.dontMock('../../utils/StringUtil');
jest.dontMock('../../utils/Util');

var MesosSummaryStore = require('../MesosSummaryStore');
var MockStates = require('./fixtures/MockStates.json');
var Service = require('../../structs/Service');

MesosSummaryStore.init();

describe('Mesos State Store', function () {

  describe('#getTaskFailureRate', function () {

    beforeEach(function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskRunning);
      // Necessary because prevMesosStatesMap is only set by getFailureRate.
      MesosSummaryStore.get('taskFailureRates');
    });

    it('is 0% initially', function () {
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });

    it('is 0% when one task finishes', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskFinished);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });

    it('is 100% when one task fails', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskFailed);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(100);
    });

    it('is 0% when one task is killed', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskKilled);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });
  });

  describe('#hasServiceUrl', function () {
    beforeEach(function () {
      this.getServiceFromName = MesosSummaryStore.getServiceFromName;
      MesosSummaryStore.getServiceFromName = function (hasUrl) {
        if (hasUrl === 'name_of_service_with_url') {
          return new Service({
            name: 'fake_service',
            webui_url: 'http://google.com'
          });
        }

        return new Service({
          name: 'fake_service'
        });
      };
    });

    afterEach(function () {
      MesosSummaryStore.getServiceFromName = this.getServiceFromName;
    });

    it('returns true if service has a web url', function () {
      var hasUrl = MesosSummaryStore.hasServiceUrl('name_of_service_with_url');

      expect(hasUrl).toEqual(true);
    });

    it('returns false if service does not have a web url', function () {
      var hasUrl = MesosSummaryStore.hasServiceUrl('name_of_service_without_url');

      expect(hasUrl).toEqual(false);
    });
  });

});
<commit_msg>Added test<commit_after>var _ = require('underscore');

jest.dontMock('../../mixins/GetSetMixin');
jest.dontMock('../MesosSummaryStore');
jest.dontMock('./fixtures/MockStates.json');
jest.dontMock('./fixtures/MockAppMetadata');
jest.dontMock('./fixtures/MockParsedAppMetadata');
jest.dontMock('../../utils/MesosSummaryUtil');
jest.dontMock('../../utils/RequestUtil');
jest.dontMock('../../utils/StringUtil');
jest.dontMock('../../utils/StringUtil');
jest.dontMock('../../utils/Util');

var MesosSummaryStore = require('../MesosSummaryStore');
var MockStates = require('./fixtures/MockStates.json');
var Service = require('../../structs/Service');

MesosSummaryStore.init();

describe('Mesos State Store', function () {

  describe('#processSummary', function () {
    beforeEach(function () {
      this.processSummaryError = MesosSummaryStore.processSummaryError;
      MesosSummaryStore.processSummaryError = jest.genMockFunction();
      MesosSummaryStore.processSummary({});
    });

    afterEach(function () {
      MesosSummaryStore.processSummaryError = this.processSummaryError;
    });

    it('calls processSummaryError with no arguments when summary is empty Object',
      function () {
        expect(MesosSummaryStore.processSummaryError.mock.calls.length).toEqual(1);
        expect(MesosSummaryStore.processSummaryError.mock.calls[0].length).toEqual(0);
      });
  });

  describe('#getTaskFailureRate', function () {

    beforeEach(function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskRunning);
      // Necessary because prevMesosStatesMap is only set by getFailureRate.
      MesosSummaryStore.get('taskFailureRates');
    });

    it('is 0% initially', function () {
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });

    it('is 0% when one task finishes', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskFinished);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });

    it('is 100% when one task fails', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskFailed);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(100);
    });

    it('is 0% when one task is killed', function () {
      MesosSummaryStore.processSummary(MockStates.oneTaskKilled);
      var taskFailureRate = MesosSummaryStore.get('taskFailureRate');
      expect(_.last(taskFailureRate).rate).toEqual(0);
    });
  });

  describe('#hasServiceUrl', function () {
    beforeEach(function () {
      this.getServiceFromName = MesosSummaryStore.getServiceFromName;
      MesosSummaryStore.getServiceFromName = function (hasUrl) {
        if (hasUrl === 'name_of_service_with_url') {
          return new Service({
            name: 'fake_service',
            webui_url: 'http://google.com'
          });
        }

        return new Service({
          name: 'fake_service'
        });
      };
    });

    afterEach(function () {
      MesosSummaryStore.getServiceFromName = this.getServiceFromName;
    });

    it('returns true if service has a web url', function () {
      var hasUrl = MesosSummaryStore.hasServiceUrl('name_of_service_with_url');

      expect(hasUrl).toEqual(true);
    });

    it('returns false if service does not have a web url', function () {
      var hasUrl = MesosSummaryStore.hasServiceUrl('name_of_service_without_url');

      expect(hasUrl).toEqual(false);
    });
  });

});
<|endoftext|>


<commit_before>var FS = require('fs');
var GM = require('gm');
var Fabric = require('fabric').fabric;
var Cloner = require('cloneextend');
var argv = require('optimist').argv;

var colorValues = {
  'red': 'rgb(193,40,11)',
  'orange': 'rgb(355,144,36)',
  'yellow': 'rgb(243,224,14)',
  'green': 'rgb(22,220,129)',
  'blue': 'rgb(35,67,232)',
  'violet': 'rgb(220,84,183)',
  'black': 'rgb(0,0,0)'
};

function usage(err) {
  console.log('Example Usage:');
  console.log('node imagemarkup.js [--help|-h] - Show this information');
  console.log('node imagemarkup.js --input infile.jpg --output outfile.jpg --markup \'[markup_string]\'');
  console.log('node imagemarkup.js --json \'[json_string]\'');
  //TODO: "See README file for specification"
  if (err)
    process.exit(err);
  else
    process.exit(0);
}

var shadows = false;

function processArgs() {
  if (argv.help || argv.h) {
    usage();
  }

  if (argv.shadows) {
    shadows = argv.shadows;
  }

  var json;
  if (argv.json && argv.markup) {
    console.error('Invalid usage.');
    usage(-1);
  } else if (argv.json) {
    json = JSON.parse(argv.json);
    cleanJSON(json);
    processImage(json);
  } else if (argv.markup) {
    if (!argv.input || !argv.output) {
      console.error('Invalid usage. Input or output path missing.');
      usage(-1);
    }
    convertMarkupToJSON(argv.markup, argv.input, argv.output);
  } else {
    console.error('Invalid uage.');
    usage(-1);
  }
}

function validateJSON(json, level) {
  String.prototype.repeat = function (num) {
    return new Array(num+1).join(this);
  }
  if (!level) {
    console.log("{");
    validateJSON(json, 1);
    console.log("}");
  } else {
    for (property in json) {
      console.log("\t".repeat(level) + property + ": " + json[property] + " [" + typeof(json[property]) + "]");
      if (typeof(json[property]) == 'object') {
        console.log("\t".repeat(level) + "{");
        validateJSON(json[property], level+1);
        console.log("\t".repeat(level) + "}");
      }
    }
  }
}

/**
 * Cycles through an object and changes all numeric fields to ints
 * where necessary.
 */
function cleanJSON(json) {
  for (property in json) {

    if (typeof(json[property]) == 'object') {
      cleanJSON(json[property]);
    }
    else if (property == 'x' || property == 'y' ||
             property == 'width' || property == 'height' ||
             property == 'radius') {
      if (typeof(json[property]) == 'string') {
        json[property] = parseInt(json[property]);
      }
    }
  }
}

function convertMarkupToJSON(markup, infile, outfile) {
  var json = {};

  GM(infile).size(function (err, size) {
    if (err) throw err;

    json['dimensions'] = size;
    json['finalDimensions'] = size;

    json['instructions'] = {};

    var instructions = markup.split(";");
    for (var i = 0; i < instructions.length; ++i) {
      if (instructions[i] == '') continue;

      var args = instructions[i].split(",");
      var command = args[0];
      switch (command) {
        case 'crop':
          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var crop = {};
          crop['from'] = from;
          crop['size'] = size;

          json['instructions']['crop'] = crop;
          json['finalDimensions'] = crop['size'];
          break;
        case 'circle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var radius = parseInt(args[2]);
          var color = args[3];

          var circle = {};
          circle['from'] = from;
          circle['radius'] = radius;
          circle['color'] = color;

          json['instructions']['draw'].push({'circle': circle});
          break;
        case 'rectangle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var color = args[3];

          var rectangle = {
            'from': from,
            'size': size,
            'color': color
          };

          json['instructions']['draw'].push({'rectangle': rectangle});
          break;
        default:
      }
    }

    json['sourceFile'] = infile;
    json['destinationFile'] = outfile;

    processImage(json);
  });
}

function processImage(json) {
  var canvas = Fabric.createCanvasForNode(json['finalDimensions']['width'],json['finalDimensions']['height']);

  var imagePath = json['sourceFile'];

  var crop = json['instructions']['crop'];
  var imageOffset = (typeof crop != "undefined") ?
   {'x':crop['from']['x'],
    'y':crop['from']['y']} :
   {'x':0,'y':0};

  function applyBackground(canvas) {
    FS.readFile(imagePath, function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(canvas);
      });
    });
  }

  function applyMarkup(canvas) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              switch (shapeName) {
                case 'rectangle':
                  shape = e[shapeName];
                  drawRectangle(json, canvas, shape, imageOffset);
                  break;
                case 'circle':
                  shape = e[shapeName];
                  drawCircle(json, canvas,shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }

    writeCanvas(canvas);
  }

  function writeCanvas(canvas) {
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
  }

  applyBackground(canvas);
}

function drawRectangle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(json['finalDimensions']['width'] / 300 * 2), 2);
  whiteStroke = Math.max(Math.round(shape['stroke'] / 4), 1);

  var rect = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    width: shape['size']['width'],
    height: shape['size']['height'],
    rx: 1,
    ry: 1,
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  }

  //Fabric調整
  rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
  rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

  var rectBorder = Cloner.clone(rect);
  rectBorder['width'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['height'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['rx'] = 5;
  rectBorder['ry'] = 5;
  rectBorder['strokeWidth'] = whiteStroke;
  rectBorder['stroke'] = 'white';

  var rectInline = Cloner.clone(rect);
  rectInline['width'] -= rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectInline['height'] -= rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectInline['rx'] = 5;
  rectInline['ry'] = 5;
  rectInline['strokeWidth'] = whiteStroke;
  rectInline['stroke'] = 'white';

  if (shadows == true) {
    var rectShadow = Cloner.clone(rect);
    rectShadow['left'] += 7;
    rectShadow['top'] += 7;
    rectShadow['stroke'] = 'rgba(0,0,0,0.5)';

    canvas.add(new Fabric.Rect(rectShadow));
  }

  canvas.add(new Fabric.Rect(rect));
  canvas.add(new Fabric.Rect(rectBorder));
  canvas.add(new Fabric.Rect(rectInline));
}

function drawCircle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(shape['radius'] / 4), 5);
  whiteStroke = 1;

  var circle = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    radius: shape['radius'],
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  };
  var circleBorder = Cloner.clone(circle);
  circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
  circleBorder['strokeWidth'] = whiteStroke;
  circleBorder['stroke'] = 'white';

  var circleInline = Cloner.clone(circleBorder);
  circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

  if (shadows == true) {
    var circleShadow = Cloner.clone(circle);
    circleShadow['left'] += 7;
    circleShadow['top'] += 7;
    circleShadow['stroke'] = 'rgba(0,0,0,0.5)';

    canvas.add(new Fabric.Circle(circleShadow));
  }

  canvas.add(new Fabric.Circle(circle));
  canvas.add(new Fabric.Circle(circleBorder));
  canvas.add(new Fabric.Circle(circleInline));
}

processArgs();
<commit_msg>Move shadow arg logic to single line<commit_after>var FS = require('fs');
var GM = require('gm');
var Fabric = require('fabric').fabric;
var Cloner = require('cloneextend');
var argv = require('optimist').argv;

var colorValues = {
  'red': 'rgb(193,40,11)',
  'orange': 'rgb(355,144,36)',
  'yellow': 'rgb(243,224,14)',
  'green': 'rgb(22,220,129)',
  'blue': 'rgb(35,67,232)',
  'violet': 'rgb(220,84,183)',
  'black': 'rgb(0,0,0)'
};

function usage(err) {
  console.log('Example Usage:');
  console.log('node imagemarkup.js [--help|-h] - Show this information');
  console.log('node imagemarkup.js --input infile.jpg --output outfile.jpg --markup \'[markup_string]\'');
  console.log('node imagemarkup.js --json \'[json_string]\'');
  //TODO: "See README file for specification"
  if (err)
    process.exit(err);
  else
    process.exit(0);
}

var shadows;

function processArgs() {
  if (argv.help || argv.h) {
    usage();
  }

  shadows = typeof(argv.shadows) == 'undefined' ? false : argv.shadows == 'true';

  var json;
  if (argv.json && argv.markup) {
    console.error('Invalid usage.');
    usage(-1);
  } else if (argv.json) {
    json = JSON.parse(argv.json);
    cleanJSON(json);
    processImage(json);
  } else if (argv.markup) {
    if (!argv.input || !argv.output) {
      console.error('Invalid usage. Input or output path missing.');
      usage(-1);
    }
    convertMarkupToJSON(argv.markup, argv.input, argv.output);
  } else {
    console.error('Invalid uage.');
    usage(-1);
  }
}

function validateJSON(json, level) {
  String.prototype.repeat = function (num) {
    return new Array(num+1).join(this);
  }
  if (!level) {
    console.log("{");
    validateJSON(json, 1);
    console.log("}");
  } else {
    for (property in json) {
      console.log("\t".repeat(level) + property + ": " + json[property] + " [" + typeof(json[property]) + "]");
      if (typeof(json[property]) == 'object') {
        console.log("\t".repeat(level) + "{");
        validateJSON(json[property], level+1);
        console.log("\t".repeat(level) + "}");
      }
    }
  }
}

/**
 * Cycles through an object and changes all numeric fields to ints
 * where necessary.
 */
function cleanJSON(json) {
  for (property in json) {

    if (typeof(json[property]) == 'object') {
      cleanJSON(json[property]);
    }
    else if (property == 'x' || property == 'y' ||
             property == 'width' || property == 'height' ||
             property == 'radius') {
      if (typeof(json[property]) == 'string') {
        json[property] = parseInt(json[property]);
      }
    }
  }
}

function convertMarkupToJSON(markup, infile, outfile) {
  var json = {};

  GM(infile).size(function (err, size) {
    if (err) throw err;

    json['dimensions'] = size;
    json['finalDimensions'] = size;

    json['instructions'] = {};

    var instructions = markup.split(";");
    for (var i = 0; i < instructions.length; ++i) {
      if (instructions[i] == '') continue;

      var args = instructions[i].split(",");
      var command = args[0];
      switch (command) {
        case 'crop':
          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var crop = {};
          crop['from'] = from;
          crop['size'] = size;

          json['instructions']['crop'] = crop;
          json['finalDimensions'] = crop['size'];
          break;
        case 'circle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var radius = parseInt(args[2]);
          var color = args[3];

          var circle = {};
          circle['from'] = from;
          circle['radius'] = radius;
          circle['color'] = color;

          json['instructions']['draw'].push({'circle': circle});
          break;
        case 'rectangle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var color = args[3];

          var rectangle = {
            'from': from,
            'size': size,
            'color': color
          };

          json['instructions']['draw'].push({'rectangle': rectangle});
          break;
        default:
      }
    }

    json['sourceFile'] = infile;
    json['destinationFile'] = outfile;

    processImage(json);
  });
}

function processImage(json) {
  var canvas = Fabric.createCanvasForNode(json['finalDimensions']['width'],json['finalDimensions']['height']);

  var imagePath = json['sourceFile'];

  var crop = json['instructions']['crop'];
  var imageOffset = (typeof crop != "undefined") ?
   {'x':crop['from']['x'],
    'y':crop['from']['y']} :
   {'x':0,'y':0};

  function applyBackground(canvas) {
    FS.readFile(imagePath, function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(canvas);
      });
    });
  }

  function applyMarkup(canvas) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              switch (shapeName) {
                case 'rectangle':
                  shape = e[shapeName];
                  drawRectangle(json, canvas, shape, imageOffset);
                  break;
                case 'circle':
                  shape = e[shapeName];
                  drawCircle(json, canvas,shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }

    writeCanvas(canvas);
  }

  function writeCanvas(canvas) {
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
  }

  applyBackground(canvas);
}

function drawRectangle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(json['finalDimensions']['width'] / 300 * 2), 2);
  whiteStroke = Math.max(Math.round(shape['stroke'] / 4), 1);

  var rect = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    width: shape['size']['width'],
    height: shape['size']['height'],
    rx: 1,
    ry: 1,
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  }

  //Fabric調整
  rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
  rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

  var rectBorder = Cloner.clone(rect);
  rectBorder['width'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['height'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['rx'] = 5;
  rectBorder['ry'] = 5;
  rectBorder['strokeWidth'] = whiteStroke;
  rectBorder['stroke'] = 'white';

  var rectInline = Cloner.clone(rect);
  rectInline['width'] -= rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectInline['height'] -= rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectInline['rx'] = 5;
  rectInline['ry'] = 5;
  rectInline['strokeWidth'] = whiteStroke;
  rectInline['stroke'] = 'white';

  if (shadows == true) {
    var rectShadow = Cloner.clone(rect);
    rectShadow['left'] += 7;
    rectShadow['top'] += 7;
    rectShadow['stroke'] = 'rgba(0,0,0,0.5)';

    canvas.add(new Fabric.Rect(rectShadow));
  }

  canvas.add(new Fabric.Rect(rect));
  canvas.add(new Fabric.Rect(rectBorder));
  canvas.add(new Fabric.Rect(rectInline));
}

function drawCircle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(shape['radius'] / 4), 5);
  whiteStroke = 1;

  var circle = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    radius: shape['radius'],
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  };
  var circleBorder = Cloner.clone(circle);
  circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
  circleBorder['strokeWidth'] = whiteStroke;
  circleBorder['stroke'] = 'white';

  var circleInline = Cloner.clone(circleBorder);
  circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

  if (shadows == true) {
    var circleShadow = Cloner.clone(circle);
    circleShadow['left'] += 7;
    circleShadow['top'] += 7;
    circleShadow['stroke'] = 'rgba(0,0,0,0.5)';

    canvas.add(new Fabric.Circle(circleShadow));
  }

  canvas.add(new Fabric.Circle(circle));
  canvas.add(new Fabric.Circle(circleBorder));
  canvas.add(new Fabric.Circle(circleInline));
}

processArgs();
<|endoftext|>


<commit_before>export function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

export function parseJSON(response) {
  return response.json()
}

export function responseText(response) {
  return response.text()
}

export function customFetch(url, options) {
  return fetch(url, options)
    .then(checkStatus)
}

function isGet(options) {
  let {method = 'GET'} = options

  return method === 'GET'
}

function buildUrl(urlStr, params){
  const url = new URL(urlStr, window.location.href)
  Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))

  return url
}

export function ajax(options) {
  let {url, data, ...opt} = options
  if (isGet(options)) {
    url = buildUrl(url, data)
  } else {
    opt = {...opt, data}
  }

  return customFetch(url, opt)
}

ajax.json = options => ajax(options).then(parseJSON)
ajax.text = options => ajax(options).then(responseText)
ajax.html = ajax.text
<commit_msg>Don't crash on get request without params<commit_after>export function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

export function parseJSON(response) {
  return response.json()
}

export function responseText(response) {
  return response.text()
}

export function customFetch(url, options) {
  return fetch(url, options)
    .then(checkStatus)
}

function isGet(options) {
  let {method = 'GET'} = options

  return method === 'GET'
}

function buildUrl(urlStr, params){
  const url = new URL(urlStr, window.location.href)
  Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))

  return url
}

export function ajax(options) {
  let {url, data = {}, ...opt} = options
  if (isGet(options)) {
    url = buildUrl(url, data)
  } else {
    opt = {...opt, data}
  }

  return customFetch(url, opt)
}

ajax.json = options => ajax(options).then(parseJSON)
ajax.text = options => ajax(options).then(responseText)
ajax.html = ajax.text
<|endoftext|>


<commit_before>'use strict';
const Q = require('q');
const mongoose = require('mongoose');
const uuid = require('node-uuid');

function isRuleAppliedToMessage(rule, message, compiledObject) {
  let isRuleApplied = false;
  console.log('rule', rule);
  console.log('message', message);
  console.log('compiledObject', compiledObject);

  if (rule && message) {
    message = message.toUpperCase();
    let valueToCompare = message;

    if (rule.field && compiledObject) {
      let compiledObjectFieldValue = compiledObject[rule.field];
      valueToCompare = (compiledObjectFieldValue || valueToCompare).toUpperCase();
    }

    if (rule.operator === 'contains') {
      isRuleApplied = valueToCompare.indexOf(rule.expectedValue.toUpperCase()) > -1;
    }
    else if (rule.operator === 'higher') {

      try {
        let intValueToCompare = parseInt(valueToCompare);
        let intExpectedValue = parseInt(rule.expectedValue);
        isRuleApplied = intValueToCompare > intExpectedValue;
      }
      catch (error) {
        console.log('errHigher', error);
        isRuleApplied = false;
      }
    }
    else {
      isRuleApplied = valueToCompare === rule.expectedValue.toUpperCase();
    }
  }

  return isRuleApplied;
}

module.exports = {
  isRuleAppliedToMessage: isRuleAppliedToMessage,
  getLogAlertsToSave: function (userId, appId, alerts, logMessages) {
    let alertsToInsert = [];
    let timer = uuid.v1();

    console.time(timer);
    console.log('getLogAlertsToSave >> Start >>', logMessages.length);

    logMessages.forEach((logMessage) => {

      alerts.forEach((alert) => {

        for (var i = 0; i < alert.rules.length; i++) {
          if (isRuleAppliedToMessage(alert.rules[i], logMessage.message, logMessage.compiledMessage)) {
            alertsToInsert.push({
              alert: alert,
              logAlert: {
                userId: mongoose.Types.ObjectId(userId),
                alertId: alert._id,
                appId: appId,
                message: logMessage.message,
                compiledMessage: logMessage.compiledMessage,
                logId: logMessage.logId,
                alertDate: new Date()
              }
            });
            break;
          }
        }
      });
    });

    console.timeEnd(timer);
    return Q.resolve(alertsToInsert);
  }
};
<commit_msg>remove log<commit_after>'use strict';
const Q = require('q');
const mongoose = require('mongoose');
const uuid = require('node-uuid');

function isRuleAppliedToMessage(rule, message, compiledObject) {
  let isRuleApplied = false;

  if (rule && message) {
    message = message.toUpperCase();
    let valueToCompare = message;

    if (rule.field && compiledObject) {
      let compiledObjectFieldValue = compiledObject[rule.field];
      valueToCompare = (compiledObjectFieldValue || valueToCompare).toUpperCase();
    }

    if (rule.operator === 'contains') {
      isRuleApplied = valueToCompare.indexOf(rule.expectedValue.toUpperCase()) > -1;
    }
    else if (rule.operator === 'higher') {

      try {
        let intValueToCompare = parseInt(valueToCompare);
        let intExpectedValue = parseInt(rule.expectedValue);
        isRuleApplied = intValueToCompare > intExpectedValue;
      }
      catch (error) {
        console.log('errHigher', error);
        isRuleApplied = false;
      }
    }
    else {
      isRuleApplied = valueToCompare === rule.expectedValue.toUpperCase();
    }
  }

  return isRuleApplied;
}

module.exports = {
  isRuleAppliedToMessage: isRuleAppliedToMessage,
  getLogAlertsToSave: function (userId, appId, alerts, logMessages) {
    let alertsToInsert = [];
    let timer = uuid.v1();

    console.time(timer);
    console.log('getLogAlertsToSave >> Start >>', logMessages.length);

    logMessages.forEach((logMessage) => {

      alerts.forEach((alert) => {

        for (var i = 0; i < alert.rules.length; i++) {
          if (isRuleAppliedToMessage(alert.rules[i], logMessage.message, logMessage.compiledMessage)) {
            alertsToInsert.push({
              alert: alert,
              logAlert: {
                userId: mongoose.Types.ObjectId(userId),
                alertId: alert._id,
                appId: appId,
                message: logMessage.message,
                compiledMessage: logMessage.compiledMessage,
                logId: logMessage.logId,
                alertDate: new Date()
              }
            });
            break;
          }
        }
      });
    });

    console.timeEnd(timer);
    return Q.resolve(alertsToInsert);
  }
};
<|endoftext|>


<commit_before>ActivityFeed = {
  collapseAll: function() {
    $$('#activities .activity, #activities .thread').invoke("addClassName", "collapsed");
    $('activities').addClassName("collapsed");
    this.collapsed = true;
  },
  expandAll: function() {
    $$('#activities .activity, #activities .thread').invoke("removeClassName", "collapsed");
    $('activities').removeClassName("collapsed");
    this.collapsed = false;
  },
  toggle: function(el) {
    el.toggleClassName("collapsed");
    Threads.select(el);
    Threads.ensureVisible(el);
  },
  collapsed: false
};

document.on("click", "a.collapsed_mode", function(e,el) {
  e.stop();
  ActivityFeed.collapseAll();
  $$('a.collapsed_mode')[0].up('.el').toggle();
  $$('a.expanded_mode')[0].up('.el').toggle();
  var r = new Ajax.Request('/account/activity_feed_mode/collapsed');
});

document.on("click", "a.expanded_mode", function(e,el) {
  e.stop();
  ActivityFeed.expandAll();
  $$('a.collapsed_mode')[0].up('.el').toggle();
  $$('a.expanded_mode')[0].up('.el').toggle();
  var r = new Ajax.Request('/account/activity_feed_mode/expanded');
});

document.on("click", "#activities .comment_header", function(e,el) {
  ActivityFeed.toggle(el.up('.thread'));
});

document.on("click", "#activities .comment_header a", function(e,el) {
  ActivityFeed.toggle(el.up('.thread'));
  if (!el.up('.project_overlay')) {
    e.stop();
    window.location = el.readAttribute("href");
  }
});

document.on("dom:loaded", function() {
  if (my_user.collapse_activities && $$('a.collapsed_mode').any()) {
    ActivityFeed.collapseAll();
    $$('a.collapsed_mode').first().up('.el').toggle();
    $$('a.expanded_mode').first().up('.el').toggle();
  }
});

<commit_msg>Allow middle click on links for thread headers<commit_after>ActivityFeed = {
  collapseAll: function() {
    $$('#activities .activity, #activities .thread').invoke("addClassName", "collapsed");
    $('activities').addClassName("collapsed");
    this.collapsed = true;
  },
  expandAll: function() {
    $$('#activities .activity, #activities .thread').invoke("removeClassName", "collapsed");
    $('activities').removeClassName("collapsed");
    this.collapsed = false;
  },
  toggle: function(el) {
    el.toggleClassName("collapsed");
    Threads.select(el);
    Threads.ensureVisible(el);
  },
  collapsed: false
};

document.on("click", "a.collapsed_mode", function(e,el) {
  e.stop();
  ActivityFeed.collapseAll();
  $$('a.collapsed_mode')[0].up('.el').toggle();
  $$('a.expanded_mode')[0].up('.el').toggle();
  var r = new Ajax.Request('/account/activity_feed_mode/collapsed');
});

document.on("click", "a.expanded_mode", function(e,el) {
  e.stop();
  ActivityFeed.expandAll();
  $$('a.collapsed_mode')[0].up('.el').toggle();
  $$('a.expanded_mode')[0].up('.el').toggle();
  var r = new Ajax.Request('/account/activity_feed_mode/expanded');
});

document.on("click", "#activities .comment_header", function(e,el) {
  ActivityFeed.toggle(el.up('.thread'));
});

document.on("click", "#activities .comment_header a", function(e,el) {
  ActivityFeed.toggle(el.up('.thread'));
  if (e.isMiddleClick()) { return; }
  if (!el.up('.project_overlay')) {
    e.stop();
    window.location = el.readAttribute("href");
  }
});

document.on("dom:loaded", function() {
  if (my_user.collapse_activities && $$('a.collapsed_mode').any()) {
    ActivityFeed.collapseAll();
    $$('a.collapsed_mode').first().up('.el').toggle();
    $$('a.expanded_mode').first().up('.el').toggle();
  }
});

<|endoftext|>


<commit_before>const keycloakRedirect = require('keycloak-redirect');

/**
 * Init Key Cloak Redirect
 * @see ./entry-point.sh for window.config declaration and assignment
 * @returns {boolean}
 * @example Local environment, update config:
 *  var config = {
 *   "keycloakUrl": 'https://keycloak.digital.homeoffice.gov.uk/auth/realms/ircbd/protocol/openid-connect/auth',
 *   "backend": 'https://api.ircbd.homeoffice.gov.uk/',
 *   "clientId": "ircbd-api"
 * };
 */
function KeycloakInitService($window) {
  'ngInject';
  var config = window.config;
  var client = new XMLHttpRequest();
  keycloakRedirect.authenticate(config, client, $window);

  return true;
}

export default {
  name: 'KeycloakInitService',
  fn: KeycloakInitService
};<commit_msg>delay the keycloak redirect to give it a chance to get a session from sails<commit_after>const keycloakRedirect = require('keycloak-redirect');

/**
 * Init Key Cloak Redirect
 * @see ./entry-point.sh for window.config declaration and assignment
 * @returns {boolean}
 * @example Local environment, update config:
 *  var config = {
 *   "keycloakUrl": 'https://keycloak.digital.homeoffice.gov.uk/auth/realms/ircbd/protocol/openid-connect/auth',
 *   "backend": 'https://api.ircbd.homeoffice.gov.uk/',
 *   "clientId": "ircbd-api"
 * };
 */
function KeycloakInitService($window) {
  'ngInject';
  var config = window.config;
  var client = new XMLHttpRequest();
  setTimeout(function () {
    keycloakRedirect.authenticate(config, client, $window);
  }, 2000);

  return true;
}

export default {
  name: 'KeycloakInitService',
  fn: KeycloakInitService
};<|endoftext|>


<commit_before>/*
 AngularJS v1.2.2
 (c) 2010-2012 Google, Inc. http://angularjs.org
 License: MIT
*/
(function(H,f,z){'use strict';var u=f.$$minErr("$resource"),A=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;f.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(D,E){function n(f,h){this.template=f;this.defaults=h||{};this.urlParams={}}function v(m,h,k){function r(d,c){var e={};c=w({},h,c);s(c,function(a,c){t(a)&&(a=a());var g;if(a&&a.charAt&&"@"==a.charAt(0)){g=d;var b=a.substr(1);if(null==b||""===b||"hasOwnProperty"===b||!A.test("."+b))throw u("badmember",b);for(var b=b.split("."),f=0,h=
b.length;f<h&&g!==z;f++){var q=b[f];g=null!==g?g[q]:z}}else g=a;e[c]=g});return e}function e(b){return b.resource}function b(b){B(b||{},this)}var F=new n(m);k=w({},G,k);s(k,function(d,c){var h=/^(POST|PUT|PATCH)$/i.test(d.method);b[c]=function(a,c,g,m){var p={},k,q,x;switch(arguments.length){case 4:x=m,q=g;case 3:case 2:if(t(c)){if(t(a)){q=a;x=c;break}q=c;x=g}else{p=a;k=c;q=g;break}case 1:t(a)?q=a:h?k=a:p=a;break;case 0:break;default:throw u("badargs",arguments.length);}var n=this instanceof b,l=
n?k:d.isArray?[]:new b(k),y={},v=d.interceptor&&d.interceptor.response||e,A=d.interceptor&&d.interceptor.responseError||z;s(d,function(b,a){"params"!=a&&("isArray"!=a&&"interceptor"!=a)&&(y[a]=B(b))});h&&(y.data=k);F.setUrlParams(y,w({},r(k,d.params||{}),p),d.url);p=D(y).then(function(a){var c=a.data,g=l.$promise;if(c){if(f.isArray(c)!==!!d.isArray)throw u("badcfg",d.isArray?"array":"object",f.isArray(c)?"array":"object");d.isArray?(l.length=0,s(c,function(a){l.push(new b(a))})):(B(c,l),l.$promise=
g)}l.$resolved=!0;a.resource=l;return a},function(a){l.$resolved=!0;(x||C)(a);return E.reject(a)});p=p.then(function(a){var c=v(a);(q||C)(c,a.headers);return c},A);return n?p:(l.$promise=p,l.$resolved=!1,l)};b.prototype["$"+c]=function(a,d,g){t(a)&&(g=d,d=a,a={});a=b[c].call(this,a,this,d,g);return a.$promise||a}});b.bind=function(b){return v(m,w({},h,b),k)};return b}var G={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},
C=f.noop,s=f.forEach,w=f.extend,B=f.copy,t=f.isFunction;n.prototype={setUrlParams:function(m,h,k){var r=this,e=k||r.template,b,n,d=r.urlParams={};s(e.split(/\W/),function(c){if("hasOwnProperty"===c)throw u("badname");!/^\d+$/.test(c)&&(c&&RegExp("(^|[^\\\\]):"+c+"(\\W|$)").test(e))&&(d[c]=!0)});e=e.replace(/\\:/g,":");h=h||{};s(r.urlParams,function(c,d){b=h.hasOwnProperty(d)?h[d]:r.defaults[d];f.isDefined(b)&&null!==b?(n=encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,
"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+d+"(\\W|$)","g"),n+"$1")):e=e.replace(RegExp("(/?):"+d+"(\\W|$)","g"),function(a,c,b){return"/"==b.charAt(0)?b:c+b})});e=e.replace(/\/+$/,"");e=e.replace(/\/\.(?=\w+($|\?))/,".");m.url=e.replace(/\/\\\./,"/.");s(h,function(b,d){r.urlParams[d]||(m.params=m.params||{},m.params[d]=b)})}};return v}])})(window,window.angular);
//# sourceMappingURL=angular-resource.min.js.map
<commit_msg>Removing leftovers<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';
//
// TODO refactor into service:form-data
//
export default Ember.Mixin.create({

  expireActionModels: function () {
    this.set('actionModelsExpiredAt', new Date());
  },

  createAccountAction: function () {
    return this.get('store').createRecord('actionCreateAccount');
  }.property('actionModelsExpiredAt'),

  createSessionAction: function () {
    return this.get('store').createRecord('actionCreateSession');
  }.property('actionModelsExpiredAt'),

  createSubscriptionAction: function () {

    var store = this.get('store');
    var paymentMethod = store.createRecord('paymentMethod');

    var billing = store.createRecord('address');
    var shipping = store.createRecord('address');
    billing.set('shouldVerify', true);
    shipping.set('shouldVerify', true);

    paymentMethod.set('billingAddress', billing);
    paymentMethod.set('billingAddressSameAsShippingAddress', true);

    return store.createRecord('actionCreateSubscription').setProperties({
      shippingAddress: shipping,
      paymentMethod: paymentMethod,
      coupon: store.createRecord('coupon'),
      giftCard: store.createRecord('giftCard')
    });

  }.property('actionModelsExpiredAt')

});
<commit_msg>Persist cart on signUpSuccess to avoid losing it if user refreshes page on step 3B<commit_after>import Ember from 'ember';
//
// TODO refactor into service:form-data
//
export default Ember.Mixin.create({

  createAccountAction: function () {
    return this.get('store').createRecord('actionCreateAccount');
  }.property(),

  createSessionAction: function () {
    return this.get('store').createRecord('actionCreateSession');
  }.property(),

  createSubscriptionAction: function () {

    var store = this.get('store');
    var paymentMethod = store.createRecord('paymentMethod');

    var billing = store.createRecord('address');
    var shipping = store.createRecord('address');
    billing.set('shouldVerify', true);
    shipping.set('shouldVerify', true);

    paymentMethod.set('billingAddress', billing);
    paymentMethod.set('billingAddressSameAsShippingAddress', true);

    return store.createRecord('actionCreateSubscription').setProperties({
      shippingAddress: shipping,
      paymentMethod: paymentMethod,
      coupon: store.createRecord('coupon'),
      giftCard: store.createRecord('giftCard')
    });

  }.property(),

});
<|endoftext|>


<commit_before>/*
 Backporting "visuallyselected" for Firefox 40 and older versions

 See also:
   http://www.hackermusings.com/2015/06/electrolysis-a-tale-of-tab-switching-and-themes/

 Usage:
   Components.utils.import(".../path/to/visuallyselectedTabs.jsm");
   visuallyselectedTabs(gBrowser);

 license: The MIT License, Copyright (c) 2015 YUKI "Piro" Hiroshi
 original:
   http://github.com/piroor/fxaddonlib-visuallyselected-tabs
*/

var EXPORTED_SYMBOLS = ['visuallyselectedTabs'];

Components.utils.import('resource://gre/modules/Services.jsm');

function visuallyselectedTabs(aTabBrowser) {
  if (!aTabBrowser ||
      aTabBrowser.localName != 'tabbrowser' ||
      aTabBrowser.__visuallyselectedTabsInstalled ||
      Services.vc.compare(Services.appinfo.platformVersion, '41.0a1') >= 0)
    return;

  aTabBrowser.__visuallyselectedTabsInstalled = true;

  function onTabSelect(aEvent) {
    var prevTab = aEvent.detail && aEvent.detail.previousTab;
    if (prevTab)
      prevTab.removeAttribute('visuallyselected');

    aEvent.originalTarget.setAttribute('visuallyselected', true);
  }

  var tabsContainer = aTabBrowser.tabContainer;
  tabsContainer.addEventListener('TabSelect', onTabSelect, false);

  var document = aTabBrowser.ownerDocuent;
  document.addEventListener('unload', function onUnload(aEvent) {
    document.removeEventListener('unload', onUnload, false);
    tabsContainer.removeEventListener('TabSelect', onTabSelect, false);
  }, false);
}
<commit_msg>Fix typo<commit_after>/*
 Backporting "visuallyselected" for Firefox 40 and older versions

 See also:
   http://www.hackermusings.com/2015/06/electrolysis-a-tale-of-tab-switching-and-themes/

 Usage:
   Components.utils.import(".../path/to/visuallyselectedTabs.jsm");
   visuallyselectedTabs(gBrowser);

 license: The MIT License, Copyright (c) 2015 YUKI "Piro" Hiroshi
 original:
   http://github.com/piroor/fxaddonlib-visuallyselected-tabs
*/

var EXPORTED_SYMBOLS = ['visuallyselectedTabs'];

Components.utils.import('resource://gre/modules/Services.jsm');

function visuallyselectedTabs(aTabBrowser) {
  if (!aTabBrowser ||
      aTabBrowser.localName != 'tabbrowser' ||
      aTabBrowser.__visuallyselectedTabsInstalled ||
      Services.vc.compare(Services.appinfo.platformVersion, '41.0a1') >= 0)
    return;

  aTabBrowser.__visuallyselectedTabsInstalled = true;

  function onTabSelect(aEvent) {
    var prevTab = aEvent.detail && aEvent.detail.previousTab;
    if (prevTab)
      prevTab.removeAttribute('visuallyselected');

    aEvent.originalTarget.setAttribute('visuallyselected', true);
  }

  var tabsContainer = aTabBrowser.tabContainer;
  tabsContainer.addEventListener('TabSelect', onTabSelect, false);

  var document = aTabBrowser.ownerDocument;
  document.addEventListener('unload', function onUnload(aEvent) {
    document.removeEventListener('unload', onUnload, false);
    tabsContainer.removeEventListener('TabSelect', onTabSelect, false);
  }, false);
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */
var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

/**
 * Note Schema
 */
var NoteSchema = new Schema({
  title: {
    type: String,
    default: 'Untitled'
  },
  contentPlain: String,
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  },
  tags: {
    type: [ String ],
    index: true
  },
  created: {
    type: Date,
    default: Date.now
  },
  modified: {
    type: Date
  }
});

mongoose.model('Note', NoteSchema);<commit_msg>Added compound index to server note model<commit_after>'use strict';

/**
 * Module dependencies
 */
var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

/**
 * Note Schema
 */
var NoteSchema = new Schema({
  title: {
    type: String,
    default: Date.now.toString()
  },
  contentPlain: {
    type: String,
    default: ''
  },
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: 'User reference is required',
    index: true
  },
  tags: {
    type: [ String ],
    index: true
  },
  created: {
    type: Date,
    default: Date.now,
    index: true
  },
  modified: {
    type: Date
  }
});

noteSchema.index({ user: 1, created: -1, tags: 1 });
mongoose.model('Note', NoteSchema);<|endoftext|>


<commit_before>import DS from 'ember-data';
import ENUMS from 'irene/enums';

export default DS.Model.extend({
  findings: DS.attr(),
  risk: DS.attr('number'),
  status: DS.attr('string'), // this is made as string because ember power select considers 0 as null value
  attackVector: DS.attr('string'),
  attackComplexity: DS.attr('string'),
  privilegesRequired: DS.attr('string'),
  userInteraction: DS.attr('string'),
  scope: DS.attr('string'),
  confidentialityImpact: DS.attr('string'),
  integrityImpact: DS.attr('string'),
  availabilityImpact: DS.attr('string'),
  cvssBase: DS.attr('string'),
  cvssVector: DS.attr('string'),
  cvssVersion: DS.attr('string'),
  analiserVersion: DS.attr('number'),
  overriddenRisk: DS.attr('string'), // this is made as string because ember power select considers 0 as null value
  overriddenRiskToProfile: DS.attr('boolean'),
  file: DS.belongsTo('security/file'),
  owasp: DS.hasMany('owasp'),
  pcidss: DS.hasMany('pcidss'),
  attachments: DS.hasMany('security/attachment'),
  vulnerability: DS.belongsTo('security/vulnerability'),

  isPassed: (function() {
    const risk = this.get("risk");
    return risk !== ENUMS.RISK.NONE;
  }).property("risk"),

  riskLabelClass: (function() {
    return this.labelClass(this.get("risk"));
  }).property("risk"),

  labelClass(risk) {
    switch (risk) {
      case ENUMS.RISK.UNKNOWN: return `is-progress`;
      case ENUMS.RISK.NONE: return `is-success`;
      case ENUMS.RISK.LOW: return `is-info`;
      case ENUMS.RISK.MEDIUM: return `is-warning`;
      case ENUMS.RISK.HIGH: return `is-danger`;
      case ENUMS.RISK.CRITICAL: return `is-critical`;
    }
  }

});
<commit_msg>adding issue path<commit_after>import DS from 'ember-data';
import ENUMS from 'irene/enums';

export default DS.Model.extend({
  findings: DS.attr(),
  risk: DS.attr('number'),
  status: DS.attr('string'), // this is made as string because ember power select considers 0 as null value. ref:https://github.com/cibernox/ember-power-select/issues/962
  attackVector: DS.attr('string'),
  attackComplexity: DS.attr('string'),
  privilegesRequired: DS.attr('string'),
  userInteraction: DS.attr('string'),
  scope: DS.attr('string'),
  confidentialityImpact: DS.attr('string'),
  integrityImpact: DS.attr('string'),
  availabilityImpact: DS.attr('string'),
  cvssBase: DS.attr('string'),
  cvssVector: DS.attr('string'),
  cvssVersion: DS.attr('string'),
  analiserVersion: DS.attr('number'),
  overriddenRisk: DS.attr('string'), // this is made as string because ember power select considers 0 as null value. ref: https://github.com/cibernox/ember-power-select/issues/962
  overriddenRiskToProfile: DS.attr('boolean'),
  file: DS.belongsTo('security/file'),
  owasp: DS.hasMany('owasp'),
  pcidss: DS.hasMany('pcidss'),
  attachments: DS.hasMany('security/attachment'),
  vulnerability: DS.belongsTo('security/vulnerability'),

  isPassed: (function() {
    const risk = this.get("risk");
    return risk !== ENUMS.RISK.NONE;
  }).property("risk"),

  riskLabelClass: (function() {
    return this.labelClass(this.get("risk"));
  }).property("risk"),

  labelClass(risk) {
    switch (risk) {
      case ENUMS.RISK.UNKNOWN: return `is-progress`;
      case ENUMS.RISK.NONE: return `is-success`;
      case ENUMS.RISK.LOW: return `is-info`;
      case ENUMS.RISK.MEDIUM: return `is-warning`;
      case ENUMS.RISK.HIGH: return `is-danger`;
      case ENUMS.RISK.CRITICAL: return `is-critical`;
    }
  }

});
<|endoftext|>