<commit_before>import $ from 'jquery';
import Modal from './Modal';
import ThemeSelector from './ThemeSelector';
import SettingsManager, { settingManifest } from '../helpers/SettingsManager';

class SettingsModal extends Modal {

  /** @type jQuery */
  $cancel;

  /** @type jQuery */
  $save;

  /** @type Object */
  settings

  constructor() {
    super('#settings-modal');

    this.buildSettings(this.$modal.find('.modal__content'));

    // Footer buttons
    this.$cancel = this.$modal.find('[data-action=cancel]');
    this.$save = this.$modal.find('[data-action=save]');

    this.$cancel.on('click', () => {
      this.hide();
    });

    this.$save.on('click', () => {
      this.save();
      this.hide();
    });
  }

  save() {
    const settings = {};

    Object.keys(this.settings).forEach((key) => {
      settings[key] = this.settings[key].value;
    });

    SettingsManager.instance.setAll(settings);
  }

  show() {
    this.updateSettings();
    super.show();
  }

  /**
   * @typedef {Object} SettingsModal~Setting
   * @property {jQuery} $element
   * @property {} value
   */

  updateSettings() {
    const settings = SettingsManager.instance.getAll();

    Object.keys(settings).forEach((key) => {
      this.settings[key].value = settings[key];
    });
  }

  buildSettings($content) {
    this.settings = {};

    const $form = $('<div />')
      .appendTo($content)
      .addClass('form');

    Object.keys(settingManifest).forEach((key) => {
      const { label, type, params } = settingManifest[key];

      const setting = this.buildSetting(type, SettingsManager.instance.get(key), params);
      this.settings[key] = setting;

      const $item = $('<div />')
        .appendTo($form)
        .addClass('form__item');

      $('<div />')
        .appendTo($item)
        .addClass('form__label')
        .text(label);

      $('<div />')
        .appendTo($item)
        .addClass('form__control')
        .append(setting.$element);
    });
  }

  /**
   * @param type
   * @param initialValue
   * @param params
   * @returns {SettingsModal~Setting}
   */
  buildSetting(type, initialValue, params) {
    switch (type) {
      case 'theme':
        return this.buildSettingTheme(initialValue, params);

      case 'slider':
        return this.buildSettingSlider(initialValue, params);

      default:
        throw new Error(`Unknown setting type ${type}`);
    }
  }

  buildSettingTheme(initialValue) {
    const themeSelector = new ThemeSelector(initialValue);

    return {
      $element: themeSelector.$selector,
      get value() {
        return themeSelector.value;
      },
      set value(newValue) {
        themeSelector.value = newValue;
      },
    };
  }

  buildSettingSlider(initialValue, { min, max, step, multiplier }) {
    const $group = $('<div />')
      .addClass('input-group input-group--range');

    const $input = $('<input />')
      .appendTo($group)
      .addClass('input-group__input')
      .attr({ type: 'range', min, max, step })
      .val(initialValue * multiplier);
      // .on('touchmove', (e) => e.stopPropagation());

    const $value = $('<div />')
      .appendTo($group)
      .addClass('input-group__value')
      .text(initialValue * multiplier);

    $input.on('input', () => $value.text($input.val()));

    return {
      $element: $group,
      get value() {
        return $input.val() / multiplier;
      },
      set value(newValue) {
        $input.val(newValue * multiplier);
        $value.text(newValue * multiplier);
      },
    };
  }

}

export default SettingsModal;
<commit_msg>Round slider value<commit_after>import $ from 'jquery';
import Modal from './Modal';
import ThemeSelector from './ThemeSelector';
import SettingsManager, { settingManifest } from '../helpers/SettingsManager';

class SettingsModal extends Modal {

  /** @type jQuery */
  $cancel;

  /** @type jQuery */
  $save;

  /** @type Object */
  settings

  constructor() {
    super('#settings-modal');

    this.buildSettings(this.$modal.find('.modal__content'));

    // Footer buttons
    this.$cancel = this.$modal.find('[data-action=cancel]');
    this.$save = this.$modal.find('[data-action=save]');

    this.$cancel.on('click', () => {
      this.hide();
    });

    this.$save.on('click', () => {
      this.save();
      this.hide();
    });
  }

  save() {
    const settings = {};

    Object.keys(this.settings).forEach((key) => {
      settings[key] = this.settings[key].value;
    });

    SettingsManager.instance.setAll(settings);
  }

  show() {
    this.updateSettings();
    super.show();
  }

  /**
   * @typedef {Object} SettingsModal~Setting
   * @property {jQuery} $element
   * @property {} value
   */

  updateSettings() {
    const settings = SettingsManager.instance.getAll();

    Object.keys(settings).forEach((key) => {
      this.settings[key].value = settings[key];
    });
  }

  buildSettings($content) {
    this.settings = {};

    const $form = $('<div />')
      .appendTo($content)
      .addClass('form');

    Object.keys(settingManifest).forEach((key) => {
      const { label, type, params } = settingManifest[key];

      const setting = this.buildSetting(type, SettingsManager.instance.get(key), params);
      this.settings[key] = setting;

      const $item = $('<div />')
        .appendTo($form)
        .addClass('form__item');

      $('<div />')
        .appendTo($item)
        .addClass('form__label')
        .text(label);

      $('<div />')
        .appendTo($item)
        .addClass('form__control')
        .append(setting.$element);
    });
  }

  /**
   * @param type
   * @param initialValue
   * @param params
   * @returns {SettingsModal~Setting}
   */
  buildSetting(type, initialValue, params) {
    switch (type) {
      case 'theme':
        return this.buildSettingTheme(initialValue, params);

      case 'slider':
        return this.buildSettingSlider(initialValue, params);

      default:
        throw new Error(`Unknown setting type ${type}`);
    }
  }

  buildSettingTheme(initialValue) {
    const themeSelector = new ThemeSelector(initialValue);

    return {
      $element: themeSelector.$selector,
      get value() {
        return themeSelector.value;
      },
      set value(newValue) {
        themeSelector.value = newValue;
      },
    };
  }

  buildSettingSlider(initialValue, { min, max, step, multiplier }) {
    const actualInitialValue = Math.round(initialValue * multiplier);

    const $group = $('<div />')
      .addClass('input-group input-group--range');

    const $input = $('<input />')
      .appendTo($group)
      .addClass('input-group__input')
      .attr({ type: 'range', min, max, step })
      .val(actualInitialValue);
      // .on('touchmove', (e) => e.stopPropagation());

    const $value = $('<div />')
      .appendTo($group)
      .addClass('input-group__value')
      .text(actualInitialValue);

    $input.on('input', () => $value.text($input.val()));

    return {
      $element: $group,
      get value() {
        return $input.val() / multiplier;
      },
      set value(newValue) {
        const actualnewValue = Math.round(newValue * multiplier);
        $input.val(actualnewValue);
        $value.text(actualnewValue);
      },
    };
  }

}

export default SettingsModal;
<|endoftext|>


<commit_before>"use strict";

const babelPresetMeteor = require("babel-preset-meteor");
const reifyPlugin = require("babel-plugin-transform-es2015-modules-reify");
const strictModulesPluginFactory =
  require("@babel/plugin-transform-modules-commonjs").default;

const babelModulesPlugin = [function () {
  const plugin = strictModulesPluginFactory.apply(this, arguments);
  // Since babel-preset-meteor uses an exact version of the
  // @babel/plugin-transform-modules-commonjs transform (6.8.0), we can be
  // sure this plugin.inherits property is indeed the
  // @babel/plugin-transform-strict-mode transform that we wish to
  // disable. Otherwise it would be difficult to know exactly what we're
  // deleting here, since plugins don't provide much identifying
  // information.
  delete plugin.inherits;
  return plugin;
}, {
  allowTopLevelThis: true,
  strictMode: false,
  loose: true
}];

exports.getDefaults = function getDefaults(features) {
  if (features &&
      features.nodeMajorVersion >= 8) {
    return getDefaultsForNode8(features);
  }

  const combined = {
    presets: [babelPresetMeteor],
    plugins: [
      [reifyPlugin, {
        generateLetDeclarations: true,
        enforceStrictMode: false
      }]
    ]
  };

  combined.plugins.push(
    require("./plugins/dynamic-import.js")
  );

  if (! (features &&
         features.runtime === false)) {
    combined.plugins.push([
      require("@babel/plugin-transform-runtime"),
      { // Avoid importing polyfills for things like Object.keys, which
        // Meteor already shims in other ways.
        polyfill: false }
    ]);
  }

  if (features) {
    if (features.react) {
      combined.presets.push(require("@babel/preset-react"));
      combined.plugins.push(
        require("@babel/plugin-proposal-class-properties")
      );
    }

    if (features.jscript) {
      combined.plugins.push(
        require("./plugins/named-function-expressions.js"),
        require("./plugins/sanitize-for-in-objects.js")
      );
    }
  }

  // Even though we use Reify to transpile `import` and `export`
  // declarations in the original source, Babel sometimes inserts its own
  // `import` declarations later on, and of course Babel knows best how to
  // compile those declarations.
  combined.plugins.push(babelModulesPlugin);

  return {
    compact: false,
    sourceMap: false,
    ast: false,
    babelrc: false,
    presets: [combined]
  };
};

function getDefaultsForNode8(features) {
  const plugins = [
    // Support Flow type syntax by simply stripping it out.
    require("@babel/plugin-syntax-flow"),
    require("@babel/plugin-transform-flow-strip-types"),

    // Compile import/export syntax with Reify.
    [reifyPlugin, {
      generateLetDeclarations: true,
      enforceStrictMode: false
    }],

    // Import helpers from the babel-runtime package rather than
    // redefining them at the top of each module.
    [require("@babel/plugin-transform-runtime"), {
      // Avoid importing polyfills for things like Object.keys, which
      // Meteor already shims in other ways.
      polyfill: false
    }],

    // Make assigning to imported symbols a syntax error.
    require("@babel/plugin-check-constants"),

    // Not fully supported in Node 8 without the --harmony flag.
    require("@babel/plugin-syntax-object-rest-spread"),
    require("@babel/plugin-proposal-object-rest-spread"),

    // Ensure that async functions run in a Fiber, while also taking
    // full advantage of native async/await support in Node 8.
    [require("./plugins/async-await.js"), {
      // Do not transform `await x` to `Promise.await(x)`, since Node
      // 8 has native support for await expressions.
      useNativeAsyncAwait: false
    }],

    // Transform `import(id)` to `module.dynamicImport(id)`.
    require("./plugins/dynamic-import.js"),

    // Enable class property syntax for server-side React code.
    require("@babel/plugin-proposal-class-properties"),

    // In case babel-plugin-transform-runtime generated any import
    // declarations after reifyPlugin ran, make sure to compile them.
    babelModulesPlugin
  ];

  const presets = [{
    plugins
  }];

  if (features) {
    if (features.react) {
      // Enable JSX syntax for server-side React code.
      presets.push(require("@babel/preset-react"));
    }
  }

  return {
    compact: false,
    sourceMap: false,
    ast: false,
    babelrc: false,
    presets
  };
}

exports.getMinifierDefaults = function getMinifierDefaults(features) {
  const options = {
    // Generate code in loose mode
    compact: false,
    // Don't generate a source map, we do that during compilation
    sourceMap: false,
    // We don't need to generate AST code
    ast: false,
    // Do not honor babelrc settings, would conflict with compilation
    babelrc: false,
    // May be modified according to provided features below.
    plugins: [],
    // Only include the minifier plugins, since we've already compiled all
    // the ECMAScript syntax we want.
    presets: [require("babel-preset-minify")]
  };

  if (features) {
    if (features.inlineNodeEnv) {
      options.plugins.push([
        require("./plugins/inline-node-env.js"),
        { nodeEnv: features.inlineNodeEnv }
      ]);
    }
  }

  return options;
};
<commit_msg>Allow disabling @babel/plugin-transform-runtime in Node 8.<commit_after>"use strict";

const babelPresetMeteor = require("babel-preset-meteor");
const reifyPlugin = require("babel-plugin-transform-es2015-modules-reify");
const strictModulesPluginFactory =
  require("@babel/plugin-transform-modules-commonjs").default;

const babelModulesPlugin = [function () {
  const plugin = strictModulesPluginFactory.apply(this, arguments);
  // Since babel-preset-meteor uses an exact version of the
  // @babel/plugin-transform-modules-commonjs transform (6.8.0), we can be
  // sure this plugin.inherits property is indeed the
  // @babel/plugin-transform-strict-mode transform that we wish to
  // disable. Otherwise it would be difficult to know exactly what we're
  // deleting here, since plugins don't provide much identifying
  // information.
  delete plugin.inherits;
  return plugin;
}, {
  allowTopLevelThis: true,
  strictMode: false,
  loose: true
}];

exports.getDefaults = function getDefaults(features) {
  if (features &&
      features.nodeMajorVersion >= 8) {
    return getDefaultsForNode8(features);
  }

  const combined = {
    presets: [babelPresetMeteor],
    plugins: [
      [reifyPlugin, {
        generateLetDeclarations: true,
        enforceStrictMode: false
      }]
    ]
  };

  combined.plugins.push(
    require("./plugins/dynamic-import.js")
  );

  if (! (features &&
         features.runtime === false)) {
    combined.plugins.push([
      require("@babel/plugin-transform-runtime"),
      { // Avoid importing polyfills for things like Object.keys, which
        // Meteor already shims in other ways.
        polyfill: false }
    ]);
  }

  if (features) {
    if (features.react) {
      combined.presets.push(require("@babel/preset-react"));
      combined.plugins.push(
        require("@babel/plugin-proposal-class-properties")
      );
    }

    if (features.jscript) {
      combined.plugins.push(
        require("./plugins/named-function-expressions.js"),
        require("./plugins/sanitize-for-in-objects.js")
      );
    }
  }

  // Even though we use Reify to transpile `import` and `export`
  // declarations in the original source, Babel sometimes inserts its own
  // `import` declarations later on, and of course Babel knows best how to
  // compile those declarations.
  combined.plugins.push(babelModulesPlugin);

  return {
    compact: false,
    sourceMap: false,
    ast: false,
    babelrc: false,
    presets: [combined]
  };
};

function getDefaultsForNode8(features) {
  const plugins = [];

  // Support Flow type syntax by simply stripping it out.
  plugins.push(
    require("@babel/plugin-syntax-flow"),
    require("@babel/plugin-transform-flow-strip-types")
  );

  // Compile import/export syntax with Reify.
  plugins.push([reifyPlugin, {
    generateLetDeclarations: true,
    enforceStrictMode: false
  }]);

  if (! (features &&
         features.runtime === false)) {
    // Import helpers from the babel-runtime package rather than
    // redefining them at the top of each module.
    plugins.push([require("@babel/plugin-transform-runtime"), {
      // Avoid importing polyfills for things like Object.keys, which
      // Meteor already shims in other ways.
      polyfill: false
    }]);
  }

  // Make assigning to imported symbols a syntax error.
  plugins.push(require("@babel/plugin-check-constants"));

  // Not fully supported in Node 8 without the --harmony flag.
  plugins.push(
    require("@babel/plugin-syntax-object-rest-spread"),
    require("@babel/plugin-proposal-object-rest-spread")
  );

  // Ensure that async functions run in a Fiber, while also taking
  // full advantage of native async/await support in Node 8.
  plugins.push([require("./plugins/async-await.js"), {
    // Do not transform `await x` to `Promise.await(x)`, since Node
    // 8 has native support for await expressions.
    useNativeAsyncAwait: false
  }]);

  // Transform `import(id)` to `module.dynamicImport(id)`.
  plugins.push(require("./plugins/dynamic-import.js"));

  // Enable class property syntax for server-side React code.
  plugins.push(require("@babel/plugin-proposal-class-properties"));

  // In case babel-plugin-transform-runtime generated any import
  // declarations after reifyPlugin ran, make sure to compile them.
  plugins.push(babelModulesPlugin);

  const presets = [{
    plugins
  }];

  if (features) {
    if (features.react) {
      // Enable JSX syntax for server-side React code.
      presets.push(require("@babel/preset-react"));
    }
  }

  return {
    compact: false,
    sourceMap: false,
    ast: false,
    babelrc: false,
    presets
  };
}

exports.getMinifierDefaults = function getMinifierDefaults(features) {
  const options = {
    // Generate code in loose mode
    compact: false,
    // Don't generate a source map, we do that during compilation
    sourceMap: false,
    // We don't need to generate AST code
    ast: false,
    // Do not honor babelrc settings, would conflict with compilation
    babelrc: false,
    // May be modified according to provided features below.
    plugins: [],
    // Only include the minifier plugins, since we've already compiled all
    // the ECMAScript syntax we want.
    presets: [require("babel-preset-minify")]
  };

  if (features) {
    if (features.inlineNodeEnv) {
      options.plugins.push([
        require("./plugins/inline-node-env.js"),
        { nodeEnv: features.inlineNodeEnv }
      ]);
    }
  }

  return options;
};
<|endoftext|>


<commit_before>//check if an image exists
function imageExists(image_url){
    var http = new XMLHttpRequest();
    http.open('HEAD', image_url, false);
    http.send();
    return http.status != 404;
}

function readMore(){
var author = $(".author-info div")[0];

if (author) {
	if (author.offsetHeight < author.scrollHeight && !$(".author-info__text").hasClass("overflow") ) {
			$(".author-more").css("display","block");
	} else {
		$(".author-more").css("display","none");
	}

}
}

function fixFeaturedMediaOffset(){
    if ($(window).width() >= 1200 && ($('.featured-media .figure').height() >= 5 )  ) {
        var mediaHeight=  $('.featured-media .figure').outerHeight();
        var sectionHeight=  $(".featured-media .ad-box").outerHeight();
        if (  (sectionHeight - mediaHeight) >= 0) {

            var mediamargin =  mediaHeight - sectionHeight + -13;
            $("section.featured-media").css("margin-bottom", mediamargin);

			if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1)  {
							$(".container-fluid aside")[0].css("margin-top", (-1 *mediamargin));
		}
        }
    } else {
        $("section.featured-media").css("margin-bottom", "inherit");
    }
}

// Wrap IIFE around your code
(function($, viewport){
    $(document).ready(function() {
        $('a.btn-comment').click(function(e){
            $('html, body').animate({
                scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top
            }, 500, function(){
                $(".comments-section button").click();
            });
            return false;
        });

readMore();

// Shows asides that contain img child element, as per the draft :has css pseudo-class described here:
// http://www.ericponto.com/blog/2015/01/10/has-pseudo-class-parent-selector/
// The polyfill is no longer working so this is now done via js
$(".aside:has(> img)").css("display", "block");

	// read more expand instead of following link

$(".author-more").click(function(e){
    e.preventDefault();
    $(this).hide();
    $(".author-info__text").addClass("overflow");
});

        //fix offset
        var windowWidth = $(window).width();

        fixFeaturedMediaOffset();

        //Do not kill the dropdowns when users click in them)
        $('.dropdown-menu').children().click(function(e){
            e.stopPropagation();
        });

        //*======= ALLOW MULTIPLE NAV ITEMS TO BE OPEN AT ONCE IN MOBILE
        if ($(window).width() < 992){
            $('.dropdown.keep-open').on({
                "shown.bs.dropdown": function() { this.closable = false; },
                    "click":             function() { this.closable = true; },
                    "hide.bs.dropdown":  function() { return this.closable; }
            });
        } else {
            // this function not related to above just piggybacking on the if statement. It pushes the main content down when latest stories is open
            var menuheight = 0;
            $('.col-sm-12 .dropdown').on('show.bs.dropdown', function(event) {
                if ($(window).width() > 991) {
                    menuheight = $(this).children(".dropdown-menu").outerHeight();
                    $(".article__header").animate({
                        marginTop: "+=" +menuheight
                    }, 250, function() {
                        // Animation complete.
                    });
                }
            });
            $('.col-sm-12 .dropdown').on('hide.bs.dropdown', function(event) {
                if ($(window).width() > 991) {
                    $(".article__header").animate({
                        marginTop: "-=" +menuheight
                    }, 250, function() {
                        // Animation complete.
                    });
                }
            });
        }
        // resets margin-top on .article__header when window resized to prevent stuck margin on resize/orientation shift
        function resizedw(){
            //modified to fire after only 100ms so that it doesn't go multiple times per resize
            // Store the window width
            // Resize Event
            // Check window width has actually changed and it's not just iOS triggering a resize event on scroll
            if ($(window).width() != windowWidth) {
                // Update the window width for next time
                windowWidth = $(window).width();
                // Do stuff here
                $(".open").removeClass("open");
                $(".article__header").css("margin-top", 0);
                fixFeaturedMediaOffset();
				readMore();
            }
            // Otherwise do nothing
        }

        var doit;
        window.onresize = function(){
            clearTimeout(doit);
            doit = setTimeout(resizedw, 100);
        };

        //Moves focus directly to search field when user begins typing
        $('.search-block').on('show.bs.dropdown', function(event) {

            setTimeout(function(){
                $('input#menu__search--input').focus();
            }, 500);
        });

        //LATEST STORIES

        jQuery.fn.reverse = function() {
            return this.pushStack(this.get().reverse(), arguments);
        };

        //GLOBALS
        var totalStoryPositions;
        var counter =0;
        var topCounter = 0;
        var bottomCounter = 0;
        var response;
        var recentItems;
        var returnedStories = [];
        var storyObjects = [];
        var firstStory;
        var topPrevCounter = 0;
        var bottomPrevCounter = 0;

        //make storyObjects global, so I don't have to keep hitting the API
        storyObjects = storyObjects;

        //Returned stories from the API
        function getLatestStories(){
            storiesRequested = 25;
            returnedStories = $.ajax({
                method: 'POST',
                url: 'http://api.thetyee.ca/v1/latest/' + storiesRequested,
                dataType: 'jsonp',
                data: response,
                crossDomain: true,
                success: function(response){
                    returnedStories = returnedStories.responseJSON.hits.hits;
                    //pass data to the create function so I can create my own story objects
                    recentItems = createStoryObjects(returnedStories);
                    return recentItems;
                },
                error: function(){
                }
            });
        }//end lateststories()

        //Format the json data for ease of manipulation
        function createStoryObjects(returnedStories, callback){
            //remove the placeholder classes
            $('.latest-stories__hed').removeClass('hed_preload_placeholder');
            $('.latest-stories__dek').removeClass('dek_preload_placeholder');

            //API data, to feed into an array of custom story objects
            recentItems = returnedStories;

            //for each item from the API, plug info into a story object
            $.each(recentItems, function(key, value){

                latestStoryImage = value._source.related_media[0].uri;

                // get the smallest image > 200px available
                var bestWidth = value._source.related_media[0].width;
                var bestHeight = value._source.related_media[0].height;
                for (var k in value._source.related_media[0].thumbnails) {
                    var thumb = value._source.related_media[0].thumbnails[k];
															if (thumb.uri.indexOf("square") > -1) { continue; }
                    thumb.uri = thumb.uri.replace("thetyee.cachefly.net", "thetyee.ca");
                    if (
                        // re-enable live to filter out not yet published thumbnails
                        //			imageExists(thumb.uri) == true &&
                        thumb.width >= 200 && thumb.width <= bestWidth) {
                            bestWidth = thumb.width;
                            bestHeight = thumb.height;
                            latestStoryImage = thumb.uri;  }
                }

                //Format the API img uri's so they don't point at cachefly
                latestStoryImage = latestStoryImage.replace("thetyee.cachefly.net", "thetyee.ca");

                //Use moment.js to format the date
                formattedDate = moment.utc(value._source.storyDate).format("DD MMM");

                //Set default values for the Story object
                var Story = {
                    urlPath : 'link',
                    hed : 'title',
                    image : 'image',
                    dek : 'test',
                    date : 'date',
                    authour : 'authour'
                };

                //Populate Story objects with API data
                Story.urlPath = value._source.path;
                Story.hed = value._source.title;
                Story.image = latestStoryImage;
                Story.dek = value._source.teaser;
                Story.date = formattedDate;
                Story.authour = value._source.byline;
                Story.imageWidth = bestWidth;
                Story.imageHeight = bestHeight;

                //Put all objects into a new array for easier handling
                storyObjects.push(Story);
            });
            // on load, populate the DOM
            $('.latest-stories__media-wrapper').each(function(key, index){
                $(this).parent().find('li').each(function(i, details){

                    if (key === 0){
                        // TODO swap this out when closer to production
                        //$(this).find('a').attr('href', '#');
                        // Old value
                        $(this).find('a').attr('href', "http://thetyee.ca" + storyObjects[topCounter].urlPath);
                        $(this).find('img').attr('src', storyObjects[topCounter].image);
                        $(this).find('img').attr('width',storyObjects[topCounter].imageWidth);
                        $(this).find('img').attr('height', storyObjects[topCounter].imageHeight);
                        $(this).find('h4').html(storyObjects[topCounter].hed);
                        $(this).find('p').html(storyObjects[topCounter].dek);
                        $(this).find('.latest-stories__date').html(storyObjects[topCounter].date);
                        $(this).find('.latest-stories__authour').html(storyObjects[topCounter].authour);
                        topCounter++;
                    }
                    if (key == 1){
                        // TODO swap this out when closer to production
                        //$(this).find('a').attr('href', '#');
                        // Old value
                        $(this).find('a').attr('href', "http://thetyee.ca" + storyObjects[bottomCounter].urlPath);
                        $(this).find('img').attr('src', storyObjects[bottomCounter].image);
                        $(this).find('img').attr('width',storyObjects[topCounter].imageWidth);
                        $(this).find('img').attr('height', storyObjects[topCounter].imageHeight);
                        $(this).find('h4').html(storyObjects[bottomCounter].hed);
                        $(this).find('p').html(storyObjects[bottomCounter].dek);
                        $(this).find('.latest-stories__date').html(storyObjects[bottomCounter].date);
                        $(this).find('.latest-stories__authour').html(storyObjects[bottomCounter].authour);
                        bottomCounter++;

                    }

                });
            });
            //control the scrolling
            scrollLatestStories();
        }

        //Make Ajax call and put data into the returnedStories var for manipulation.
        getLatestStories();

        //takes parameters of current slider
        function scrollLatestStories(){
            //Get the next group of stories on click

            $('.latest-stories__media-wrapper').each(function(key, index){
                $(this).on('click', '.next', function(event){

                    $(this).parent().find('li').each(function(i, details){

                        if (key === 0){
                            $(this).find('a').attr('href', storyObjects[topCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[topCounter].image);
                            $(this).find('h4').html(storyObjects[topCounter].hed);
                            $(this).find('p').html(storyObjects[topCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[topCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[topCounter].authour);

                            topCounter++;

                            if (topCounter >= storiesRequested){
                                topCounter = 0;
                            }

                        }

                        if (key == 1){

                            $(this).find('a').attr('href', storyObjects[bottomCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[bottomCounter].image);
                            $(this).find('h4').html(storyObjects[bottomCounter].hed);
                            $(this).find('p').html(storyObjects[bottomCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[bottomCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[bottomCounter].authour);
                            bottomCounter++;

                            if (bottomCounter >= storiesRequested){
                                bottomCounter = 0;
                            }
                        }

                    });
                    //reset the counter so I can scroll the other way
                    topPrevCounter = topCounter - 7;
                    bottomPrevCounter = bottomCounter - 7;

                    //reset the click event

                });

                $(this).on('click', '.prev', function(event){
                    $(this).parent().find('li').reverse().each(function(i, details){
                        //infinite backwards scroll
                        if (topPrevCounter <=0){
                            topPrevCounter = 49;
                        }
                        if (key === 0){
                            $(this).find('a').attr('href', storyObjects[topPrevCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[topPrevCounter].image);
                            $(this).find('h4').html(storyObjects[topPrevCounter].hed);
                            $(this).find('p').html(storyObjects[topPrevCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[topPrevCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[topPrevCounter].authour);
                            topPrevCounter--;
                        }

                        if (bottomPrevCounter <= 0){
                            bottomPrevCounter = 49;
                        }

                        if (key == 1){
                            $(this).find('a').attr('href', storyObjects[bottomPrevCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[bottomPrevCounter].image);
                            $(this).find('h4').html(storyObjects[bottomPrevCounter].hed);
                            $(this).find('p').html(storyObjects[bottomPrevCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[bottomPrevCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[bottomPrevCounter].authour);
                            bottomPrevCounter--;
                        }

                    });
                    //reset the counter so i can go the other way
                    topCounter = topPrevCounter + 7;
                    bottomCounter = bottomPrevCounter + 7;
                    //reset the counter for infinite scrolling

                });
            });

        }//END LATEST STORIES SCROLLER

        // Show Disqus comments
        $(".comments-section .btn").click(function(e) {
            e.preventDefault();
            var el = $('.comments-section');
            var disqus_div = $('#disqus_thread');
            var anim_height = disqus_div.height() + 40;
            el.css({
                "height": 400,
                "max-height": 9999
            })
            .animate({
                "height": anim_height
            });

            // fade out read-more
            $('.read-more').fadeOut();
        });

    });

})(jQuery, ResponsiveBootstrapToolkit);
<commit_msg>fixed media safari adjustment v2<commit_after>//check if an image exists
function imageExists(image_url){
    var http = new XMLHttpRequest();
    http.open('HEAD', image_url, false);
    http.send();
    return http.status != 404;
}

function readMore(){
var author = $(".author-info div")[0];

if (author) {
	if (author.offsetHeight < author.scrollHeight && !$(".author-info__text").hasClass("overflow") ) {
			$(".author-more").css("display","block");
	} else {
		$(".author-more").css("display","none");
	}

}
}

function fixFeaturedMediaOffset(){
    if ($(window).width() >= 1200 && ($('.featured-media .figure').height() >= 5 )  ) {
        var mediaHeight=  $('.featured-media .figure').outerHeight();
        var sectionHeight=  $(".featured-media .ad-box").outerHeight();
        if (  (sectionHeight - mediaHeight) >= 0) {

            var mediamargin =  mediaHeight - sectionHeight + -13;
            $("section.featured-media").css("margin-bottom", mediamargin);

			if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1)  {
							var negmargin = -1 * mediamargin;
							$($("article .container-fluid aside")[0]).css("margin-top", negmargin);
		}
        }
    } else {
        $("section.featured-media").css("margin-bottom", "inherit");
    }
}

// Wrap IIFE around your code
(function($, viewport){
    $(document).ready(function() {
        $('a.btn-comment').click(function(e){
            $('html, body').animate({
                scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top
            }, 500, function(){
                $(".comments-section button").click();
            });
            return false;
        });

readMore();

// Shows asides that contain img child element, as per the draft :has css pseudo-class described here:
// http://www.ericponto.com/blog/2015/01/10/has-pseudo-class-parent-selector/
// The polyfill is no longer working so this is now done via js
$(".aside:has(> img)").css("display", "block");

	// read more expand instead of following link

$(".author-more").click(function(e){
    e.preventDefault();
    $(this).hide();
    $(".author-info__text").addClass("overflow");
});

        //fix offset
        var windowWidth = $(window).width();

        fixFeaturedMediaOffset();

        //Do not kill the dropdowns when users click in them)
        $('.dropdown-menu').children().click(function(e){
            e.stopPropagation();
        });

        //*======= ALLOW MULTIPLE NAV ITEMS TO BE OPEN AT ONCE IN MOBILE
        if ($(window).width() < 992){
            $('.dropdown.keep-open').on({
                "shown.bs.dropdown": function() { this.closable = false; },
                    "click":             function() { this.closable = true; },
                    "hide.bs.dropdown":  function() { return this.closable; }
            });
        } else {
            // this function not related to above just piggybacking on the if statement. It pushes the main content down when latest stories is open
            var menuheight = 0;
            $('.col-sm-12 .dropdown').on('show.bs.dropdown', function(event) {
                if ($(window).width() > 991) {
                    menuheight = $(this).children(".dropdown-menu").outerHeight();
                    $(".article__header").animate({
                        marginTop: "+=" +menuheight
                    }, 250, function() {
                        // Animation complete.
                    });
                }
            });
            $('.col-sm-12 .dropdown').on('hide.bs.dropdown', function(event) {
                if ($(window).width() > 991) {
                    $(".article__header").animate({
                        marginTop: "-=" +menuheight
                    }, 250, function() {
                        // Animation complete.
                    });
                }
            });
        }
        // resets margin-top on .article__header when window resized to prevent stuck margin on resize/orientation shift
        function resizedw(){
            //modified to fire after only 100ms so that it doesn't go multiple times per resize
            // Store the window width
            // Resize Event
            // Check window width has actually changed and it's not just iOS triggering a resize event on scroll
            if ($(window).width() != windowWidth) {
                // Update the window width for next time
                windowWidth = $(window).width();
                // Do stuff here
                $(".open").removeClass("open");
                $(".article__header").css("margin-top", 0);
                fixFeaturedMediaOffset();
				readMore();
            }
            // Otherwise do nothing
        }

        var doit;
        window.onresize = function(){
            clearTimeout(doit);
            doit = setTimeout(resizedw, 100);
        };

        //Moves focus directly to search field when user begins typing
        $('.search-block').on('show.bs.dropdown', function(event) {

            setTimeout(function(){
                $('input#menu__search--input').focus();
            }, 500);
        });

        //LATEST STORIES

        jQuery.fn.reverse = function() {
            return this.pushStack(this.get().reverse(), arguments);
        };

        //GLOBALS
        var totalStoryPositions;
        var counter =0;
        var topCounter = 0;
        var bottomCounter = 0;
        var response;
        var recentItems;
        var returnedStories = [];
        var storyObjects = [];
        var firstStory;
        var topPrevCounter = 0;
        var bottomPrevCounter = 0;

        //make storyObjects global, so I don't have to keep hitting the API
        storyObjects = storyObjects;

        //Returned stories from the API
        function getLatestStories(){
            storiesRequested = 25;
            returnedStories = $.ajax({
                method: 'POST',
                url: 'http://api.thetyee.ca/v1/latest/' + storiesRequested,
                dataType: 'jsonp',
                data: response,
                crossDomain: true,
                success: function(response){
                    returnedStories = returnedStories.responseJSON.hits.hits;
                    //pass data to the create function so I can create my own story objects
                    recentItems = createStoryObjects(returnedStories);
                    return recentItems;
                },
                error: function(){
                }
            });
        }//end lateststories()

        //Format the json data for ease of manipulation
        function createStoryObjects(returnedStories, callback){
            //remove the placeholder classes
            $('.latest-stories__hed').removeClass('hed_preload_placeholder');
            $('.latest-stories__dek').removeClass('dek_preload_placeholder');

            //API data, to feed into an array of custom story objects
            recentItems = returnedStories;

            //for each item from the API, plug info into a story object
            $.each(recentItems, function(key, value){

                latestStoryImage = value._source.related_media[0].uri;

                // get the smallest image > 200px available
                var bestWidth = value._source.related_media[0].width;
                var bestHeight = value._source.related_media[0].height;
                for (var k in value._source.related_media[0].thumbnails) {
                    var thumb = value._source.related_media[0].thumbnails[k];
															if (thumb.uri.indexOf("square") > -1) { continue; }
                    thumb.uri = thumb.uri.replace("thetyee.cachefly.net", "thetyee.ca");
                    if (
                        // re-enable live to filter out not yet published thumbnails
                        //			imageExists(thumb.uri) == true &&
                        thumb.width >= 200 && thumb.width <= bestWidth) {
                            bestWidth = thumb.width;
                            bestHeight = thumb.height;
                            latestStoryImage = thumb.uri;  }
                }

                //Format the API img uri's so they don't point at cachefly
                latestStoryImage = latestStoryImage.replace("thetyee.cachefly.net", "thetyee.ca");

                //Use moment.js to format the date
                formattedDate = moment.utc(value._source.storyDate).format("DD MMM");

                //Set default values for the Story object
                var Story = {
                    urlPath : 'link',
                    hed : 'title',
                    image : 'image',
                    dek : 'test',
                    date : 'date',
                    authour : 'authour'
                };

                //Populate Story objects with API data
                Story.urlPath = value._source.path;
                Story.hed = value._source.title;
                Story.image = latestStoryImage;
                Story.dek = value._source.teaser;
                Story.date = formattedDate;
                Story.authour = value._source.byline;
                Story.imageWidth = bestWidth;
                Story.imageHeight = bestHeight;

                //Put all objects into a new array for easier handling
                storyObjects.push(Story);
            });
            // on load, populate the DOM
            $('.latest-stories__media-wrapper').each(function(key, index){
                $(this).parent().find('li').each(function(i, details){

                    if (key === 0){
                        // TODO swap this out when closer to production
                        //$(this).find('a').attr('href', '#');
                        // Old value
                        $(this).find('a').attr('href', "http://thetyee.ca" + storyObjects[topCounter].urlPath);
                        $(this).find('img').attr('src', storyObjects[topCounter].image);
                        $(this).find('img').attr('width',storyObjects[topCounter].imageWidth);
                        $(this).find('img').attr('height', storyObjects[topCounter].imageHeight);
                        $(this).find('h4').html(storyObjects[topCounter].hed);
                        $(this).find('p').html(storyObjects[topCounter].dek);
                        $(this).find('.latest-stories__date').html(storyObjects[topCounter].date);
                        $(this).find('.latest-stories__authour').html(storyObjects[topCounter].authour);
                        topCounter++;
                    }
                    if (key == 1){
                        // TODO swap this out when closer to production
                        //$(this).find('a').attr('href', '#');
                        // Old value
                        $(this).find('a').attr('href', "http://thetyee.ca" + storyObjects[bottomCounter].urlPath);
                        $(this).find('img').attr('src', storyObjects[bottomCounter].image);
                        $(this).find('img').attr('width',storyObjects[topCounter].imageWidth);
                        $(this).find('img').attr('height', storyObjects[topCounter].imageHeight);
                        $(this).find('h4').html(storyObjects[bottomCounter].hed);
                        $(this).find('p').html(storyObjects[bottomCounter].dek);
                        $(this).find('.latest-stories__date').html(storyObjects[bottomCounter].date);
                        $(this).find('.latest-stories__authour').html(storyObjects[bottomCounter].authour);
                        bottomCounter++;

                    }

                });
            });
            //control the scrolling
            scrollLatestStories();
        }

        //Make Ajax call and put data into the returnedStories var for manipulation.
        getLatestStories();

        //takes parameters of current slider
        function scrollLatestStories(){
            //Get the next group of stories on click

            $('.latest-stories__media-wrapper').each(function(key, index){
                $(this).on('click', '.next', function(event){

                    $(this).parent().find('li').each(function(i, details){

                        if (key === 0){
                            $(this).find('a').attr('href', storyObjects[topCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[topCounter].image);
                            $(this).find('h4').html(storyObjects[topCounter].hed);
                            $(this).find('p').html(storyObjects[topCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[topCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[topCounter].authour);

                            topCounter++;

                            if (topCounter >= storiesRequested){
                                topCounter = 0;
                            }

                        }

                        if (key == 1){

                            $(this).find('a').attr('href', storyObjects[bottomCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[bottomCounter].image);
                            $(this).find('h4').html(storyObjects[bottomCounter].hed);
                            $(this).find('p').html(storyObjects[bottomCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[bottomCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[bottomCounter].authour);
                            bottomCounter++;

                            if (bottomCounter >= storiesRequested){
                                bottomCounter = 0;
                            }
                        }

                    });
                    //reset the counter so I can scroll the other way
                    topPrevCounter = topCounter - 7;
                    bottomPrevCounter = bottomCounter - 7;

                    //reset the click event

                });

                $(this).on('click', '.prev', function(event){
                    $(this).parent().find('li').reverse().each(function(i, details){
                        //infinite backwards scroll
                        if (topPrevCounter <=0){
                            topPrevCounter = 49;
                        }
                        if (key === 0){
                            $(this).find('a').attr('href', storyObjects[topPrevCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[topPrevCounter].image);
                            $(this).find('h4').html(storyObjects[topPrevCounter].hed);
                            $(this).find('p').html(storyObjects[topPrevCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[topPrevCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[topPrevCounter].authour);
                            topPrevCounter--;
                        }

                        if (bottomPrevCounter <= 0){
                            bottomPrevCounter = 49;
                        }

                        if (key == 1){
                            $(this).find('a').attr('href', storyObjects[bottomPrevCounter].urlPath);
                            $(this).find('img').attr('src', storyObjects[bottomPrevCounter].image);
                            $(this).find('h4').html(storyObjects[bottomPrevCounter].hed);
                            $(this).find('p').html(storyObjects[bottomPrevCounter].dek);
                            $(this).find('.latest-stories__date').html(storyObjects[bottomPrevCounter].date);
                            $(this).find('.latest-stories__authour').html(storyObjects[bottomPrevCounter].authour);
                            bottomPrevCounter--;
                        }

                    });
                    //reset the counter so i can go the other way
                    topCounter = topPrevCounter + 7;
                    bottomCounter = bottomPrevCounter + 7;
                    //reset the counter for infinite scrolling

                });
            });

        }//END LATEST STORIES SCROLLER

        // Show Disqus comments
        $(".comments-section .btn").click(function(e) {
            e.preventDefault();
            var el = $('.comments-section');
            var disqus_div = $('#disqus_thread');
            var anim_height = disqus_div.height() + 40;
            el.css({
                "height": 400,
                "max-height": 9999
            })
            .animate({
                "height": anim_height
            });

            // fade out read-more
            $('.read-more').fadeOut();
        });

    });

})(jQuery, ResponsiveBootstrapToolkit);
<|endoftext|>


<commit_before>define([
  'require',
  './lib/widget',
  './lib/baseClient',
  './lib/store',
  './lib/sync',
  './lib/wireClient',
  './lib/nodeConnect',
  './lib/util'
], function(require, widget, baseClient, store, sync, wireClient, nodeConnect, util) {

  "use strict";

  var claimedModules = {}, modules = {};

  var logger = util.getLogger('base');

  function deprecate(oldFn, newFn) {
    logger.error("DEPRECATION: " + oldFn + " is deprecated! Use " + newFn + " instead.");
  }

  /**
     @module remoteStorage
  */
  var remoteStorage =  {

    /**
     ** PUBLIC METHODS
     **/

    /**
       @method defineModule
       @memberof module:remoteStorage

       @desc Define a new module, with given name.
       Module names MUST be unique. The given builder will be called
       immediately, with two arguments, which are both instances of
       baseClient. The first accesses the private section of a modules
       storage space, the second the public one. The public area can
       be read by any client (not just an authenticated one), while
       it can only be written by an authenticated client with read-write
       access claimed on it.

       The builder is expected to return an object, as described under
       remoteStorage.getModuleInfo.

       @param {String} moduleName Name of the module to define. SHOULD be a-z and all lowercase.
       @param {Function} builder Builder function that holds the module definition.
       @see remoteStorage.getModuleInfo
     */
    defineModule: function(moduleName, builder) {
      logger.debug('DEFINE MODULE', moduleName);
      var module = builder(
        // private client:
        baseClient.getInstance(moduleName, false),
        // public client:
        baseClient.getInstance(moduleName, true)
      );
      modules[moduleName] = module;
      this[moduleName] = module.exports;
      logger.debug('Module defined: ' + moduleName, module, this);
    },

    /**
       @method getModuleList
       @memberof module:remoteStorage

       @desc Get an Array of all moduleNames, currently defined.
     */
    getModuleList: function() {
      return Object.keys(modules);
    },

    /**
       @method getClaimedModuleList
       @memberof module:remoteStorage

       @desc Get a list of all modules, currently claimed access on.
    */
    getClaimedModuleList: function() {
      return Object.keys(claimedModules);
    },

    /**
       @method getModuleInfo
       @memberof module:remoteStorage
       @summary Retrieve meta-information about a given module.

       @desc If the module doesn't exist, the result will be undefined.

       Module information currently gives you the following (if you're lucky):

       * exports - don't ever use this. it's basically the module's instance.
       * name - the name of the module, but you knew that already.
       * dataHints - an object, describing internas about the module.

       Some of the dataHints used are:

         objectType <type> - description of an object
                             type implemented by the module:
           "objectType message"

         <attributeType> <objectType>#<attribute> - description of an attribute

           "string message#subject"

         directory <path> - description of a path's purpose

           "directory documents/notes/"

         item <path> - description of a special item

           "item documents/notes/calendar"

       Hope this helps.

       @param {String} moduleName Name of the module to get information about.
     */
    getModuleInfo: function(moduleName) {
      return modules[moduleName];
    },

    /**
       @method claimAccess
       @memberof module:remoteStorage
       @summary Claim access for a set of modules.
       @desc
       You need to claim access to a module before you can
       access data from it.

       Modules can be specified in two ways:

       * via a string plus access mode for single modules:

         remoteStorage.claimAccess('contacts', 'r');

       * via an object for multiple modules:

         remoteStorage.claimAccess({
           contacts: 'r',
           documents: 'rw',
           money: 'r'
         });

       Access mode can be 'r' for read-only or 'rw' for read-write.

       Errors:

       claimAccess() will throw an exception, if any given module hasn't been
       defined (yet). Access to all previously processed modules will have been
       claimed, however.

       @param {Object|String} moduleName See description for details.
       @param {String} [mode] See description for details.
     */
    claimAccess: function(moduleName, mode) {

      var modeTestRegex = /^rw?$/;
      function testMode(moduleName, mode) {
        if(!modeTestRegex.test(mode)) {
          throw "Claimed access to module '" + moduleName + "' but mode not correctly specified ('" + mode + "').";
        }
      }

      var moduleObj;
      if(typeof moduleName === 'object') {
        moduleObj = moduleName;
      } else {
        testMode(moduleName, mode);
        moduleObj = {};
        moduleObj[moduleName] = mode
      }
      for(var _moduleName in moduleObj) {
        var _mode = moduleObj[_moduleName];
        testMode(_moduleName, _mode);
        this.claimModuleAccess(_moduleName, _mode);
      }
    },

    /** @private */
    claimModuleAccess: function(moduleName, mode) {
      logger.debug('claimModuleAccess', moduleName, mode);
      if(!(moduleName in modules)) {
        throw "Module not defined: " + moduleName;
      }

      if(moduleName in claimedModules) {
        return;
      }

      if(! mode) {
        mode = 'r';
      }
      if(moduleName == 'root') {
        moduleName = '';
        widget.addScope('', mode);
        baseClient.claimAccess('/', mode);
      } else {
        widget.addScope(moduleName, mode);
        baseClient.claimAccess('/'+moduleName+'/', mode);
        baseClient.claimAccess('/public/'+moduleName+'/', mode);
      }
      claimedModules[moduleName] = true;
    },

    /**
       @method loadModule
       @memberof module:remoteStorage
       @deprecated Use claimAccess instead.
    */
    loadModule: function() {
      deprecate('remoteStorage.loadModule', 'remoteStorage.claimAccess');
      this.claimModuleAccess.apply(this, arguments);
    },

    /** @private */
    setBearerToken: function(bearerToken, claimedScopes) {
      wireClient.setBearerToken(bearerToken);
      baseClient.claimScopes(claimedScopes);
    },

    /**
     ** DELEGATED METHODS
     **/

    disconnectRemote : wireClient.disconnectRemote,

    /**
       @method flushLocal
       @memberof module:remoteStorage

       @summary Forget this ever happened.

       @desc Delete all locally stored data.
       This doesn't clear localStorage, just removes everything
       remoteStorage.js ever saved there (though obviously only under
       the current origin).

       To implement logging out, use (at least) this.
    */
    flushLocal       : store.forgetAll,

    /**
       @method syncNow
       @memberof module:remoteStorage

       @summary Synchronize local <-> remote storage.

       @desc Syncing starts at given path and bubbles down.
       The actual changes to either local or remote storage happen in the
       future, so you should attach change handlers on the modules you're
       interested in.

       Example:
         remoteStorage.money.on('change', function(changeEvent) {
           updateBudget(changeEvent);
         });
         remoteStorage.syncNow('/money');

       Modules may bring their own sync method, which should take preference
       over the one here.

     */
    syncNow          : sync.syncNow,

    // documented in widget.
    displayWidget    : widget.display,

    getWidgetState   : widget.getState,
    setStorageInfo   : wireClient.setStorageInfo,
    getStorageHref   : wireClient.getStorageHref,

    nodeConnect: nodeConnect,

    util: util

  };

  return remoteStorage;
});
<commit_msg>remove default access mode - is now forced to set by app dev<commit_after>define([
  'require',
  './lib/widget',
  './lib/baseClient',
  './lib/store',
  './lib/sync',
  './lib/wireClient',
  './lib/nodeConnect',
  './lib/util'
], function(require, widget, baseClient, store, sync, wireClient, nodeConnect, util) {

  "use strict";

  var claimedModules = {}, modules = {};

  var logger = util.getLogger('base');

  function deprecate(oldFn, newFn) {
    logger.error("DEPRECATION: " + oldFn + " is deprecated! Use " + newFn + " instead.");
  }

  /**
     @module remoteStorage
  */
  var remoteStorage =  {

    /**
     ** PUBLIC METHODS
     **/

    /**
       @method defineModule
       @memberof module:remoteStorage

       @desc Define a new module, with given name.
       Module names MUST be unique. The given builder will be called
       immediately, with two arguments, which are both instances of
       baseClient. The first accesses the private section of a modules
       storage space, the second the public one. The public area can
       be read by any client (not just an authenticated one), while
       it can only be written by an authenticated client with read-write
       access claimed on it.

       The builder is expected to return an object, as described under
       remoteStorage.getModuleInfo.

       @param {String} moduleName Name of the module to define. SHOULD be a-z and all lowercase.
       @param {Function} builder Builder function that holds the module definition.
       @see remoteStorage.getModuleInfo
     */
    defineModule: function(moduleName, builder) {
      logger.debug('DEFINE MODULE', moduleName);
      var module = builder(
        // private client:
        baseClient.getInstance(moduleName, false),
        // public client:
        baseClient.getInstance(moduleName, true)
      );
      modules[moduleName] = module;
      this[moduleName] = module.exports;
      logger.debug('Module defined: ' + moduleName, module, this);
    },

    /**
       @method getModuleList
       @memberof module:remoteStorage

       @desc Get an Array of all moduleNames, currently defined.
     */
    getModuleList: function() {
      return Object.keys(modules);
    },

    /**
       @method getClaimedModuleList
       @memberof module:remoteStorage

       @desc Get a list of all modules, currently claimed access on.
    */
    getClaimedModuleList: function() {
      return Object.keys(claimedModules);
    },

    /**
       @method getModuleInfo
       @memberof module:remoteStorage
       @summary Retrieve meta-information about a given module.

       @desc If the module doesn't exist, the result will be undefined.

       Module information currently gives you the following (if you're lucky):

       * exports - don't ever use this. it's basically the module's instance.
       * name - the name of the module, but you knew that already.
       * dataHints - an object, describing internas about the module.

       Some of the dataHints used are:

         objectType <type> - description of an object
                             type implemented by the module:
           "objectType message"

         <attributeType> <objectType>#<attribute> - description of an attribute

           "string message#subject"

         directory <path> - description of a path's purpose

           "directory documents/notes/"

         item <path> - description of a special item

           "item documents/notes/calendar"

       Hope this helps.

       @param {String} moduleName Name of the module to get information about.
     */
    getModuleInfo: function(moduleName) {
      return modules[moduleName];
    },

    /**
       @method claimAccess
       @memberof module:remoteStorage
       @summary Claim access for a set of modules.
       @desc
       You need to claim access to a module before you can
       access data from it.

       Modules can be specified in two ways:

       * via a string plus access mode for single modules:

         remoteStorage.claimAccess('contacts', 'r');

       * via an object for multiple modules:

         remoteStorage.claimAccess({
           contacts: 'r',
           documents: 'rw',
           money: 'r'
         });

       Access mode can be 'r' for read-only or 'rw' for read-write.

       Errors:

       claimAccess() will throw an exception, if any given module hasn't been
       defined (yet). Access to all previously processed modules will have been
       claimed, however.

       @param {Object|String} moduleName See description for details.
       @param {String} [mode] See description for details.
     */
    claimAccess: function(moduleName, mode) {

      var modeTestRegex = /^rw?$/;
      function testMode(moduleName, mode) {
        if(!modeTestRegex.test(mode)) {
          throw "Claimed access to module '" + moduleName + "' but mode not correctly specified ('" + mode + "').";
        }
      }

      var moduleObj;
      if(typeof moduleName === 'object') {
        moduleObj = moduleName;
      } else {
        testMode(moduleName, mode);
        moduleObj = {};
        moduleObj[moduleName] = mode
      }
      for(var _moduleName in moduleObj) {
        var _mode = moduleObj[_moduleName];
        testMode(_moduleName, _mode);
        this.claimModuleAccess(_moduleName, _mode);
      }
    },

    /** @private */
    claimModuleAccess: function(moduleName, mode) {
      logger.debug('claimModuleAccess', moduleName, mode);
      if(!(moduleName in modules)) {
        throw "Module not defined: " + moduleName;
      }

      if(moduleName in claimedModules) {
        return;
      }

      if(moduleName == 'root') {
        moduleName = '';
        widget.addScope('', mode);
        baseClient.claimAccess('/', mode);
      } else {
        widget.addScope(moduleName, mode);
        baseClient.claimAccess('/'+moduleName+'/', mode);
        baseClient.claimAccess('/public/'+moduleName+'/', mode);
      }
      claimedModules[moduleName] = true;
    },

    /**
       @method loadModule
       @memberof module:remoteStorage
       @deprecated Use claimAccess instead.
    */
    loadModule: function() {
      deprecate('remoteStorage.loadModule', 'remoteStorage.claimAccess');
      this.claimModuleAccess.apply(this, arguments);
    },

    /** @private */
    setBearerToken: function(bearerToken, claimedScopes) {
      wireClient.setBearerToken(bearerToken);
      baseClient.claimScopes(claimedScopes);
    },

    /**
     ** DELEGATED METHODS
     **/

    disconnectRemote : wireClient.disconnectRemote,

    /**
       @method flushLocal
       @memberof module:remoteStorage

       @summary Forget this ever happened.

       @desc Delete all locally stored data.
       This doesn't clear localStorage, just removes everything
       remoteStorage.js ever saved there (though obviously only under
       the current origin).

       To implement logging out, use (at least) this.
    */
    flushLocal       : store.forgetAll,

    /**
       @method syncNow
       @memberof module:remoteStorage

       @summary Synchronize local <-> remote storage.

       @desc Syncing starts at given path and bubbles down.
       The actual changes to either local or remote storage happen in the
       future, so you should attach change handlers on the modules you're
       interested in.

       Example:
         remoteStorage.money.on('change', function(changeEvent) {
           updateBudget(changeEvent);
         });
         remoteStorage.syncNow('/money');

       Modules may bring their own sync method, which should take preference
       over the one here.

     */
    syncNow          : sync.syncNow,

    // documented in widget.
    displayWidget    : widget.display,

    getWidgetState   : widget.getState,
    setStorageInfo   : wireClient.setStorageInfo,
    getStorageHref   : wireClient.getStorageHref,

    nodeConnect: nodeConnect,

    util: util

  };

  return remoteStorage;
});
<|endoftext|>


<commit_before>function getGridTable() {
    return this.tcmGridTableSelector || (this.tcmGridTableSelector = $("#TCMGridTable"));
}
<commit_msg>Persistance<commit_after>function getGridTable() {
    return this.tcmGridTableSelector || (this.tcmGridTableSelector = $("#TCMGridTable"));
}

function HolidaysHelper(year) {
    this.pascua = this.calcularPascua(year)
}

HolidaysHelper.DayOfWeek = {};
var daysArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
for (var i = 0, length = daysArray.length; i < length; ++i) HolidaysHelper.DayOfWeek[daysArray[i]] = i;

HolidaysHelper.prototype.siguienteDiaSemana = function (diaSemana, fechaInicial, haciaAtras, daysToAdd) {
    var fecha = new Date(fechaInicial.getTime()),
        dayAddition = (haciaAtras ? -1 : 1);

    while (fecha.getDay() != diaSemana)
        fecha.setDate(fecha.getDate() + dayAddition);

    if (daysToAdd) {
        fecha.setDate(fecha.getDate() + daysToAdd)
    }
    return fecha;
};

HolidaysHelper.prototype.anteriorDiaSemana = function (diaSemana, fecha, daysToAdd) {
    return this.siguienteDiaSemana(diaSemana, fecha, true, daysToAdd)
};

HolidaysHelper.prototype.calcularPascua = function (year) {
    var a, b, c, d, e;
    var m = 24, n = 5;

    if (year >= 1583 && year <= 1699) {
        m = 22;
        n = 2;
    }
    else if (year >= 1700 && year <= 1799) {
        m = 23;
        n = 3;
    }
    else if (year >= 1800 && year <= 1899) {
        m = 23;
        n = 4;
    }
    else if (year >= 1900 && year <= 2099) {
        m = 24;
        n = 5;
    }
    else if (year >= 2100 && year <= 2199) {
        m = 24;
        n = 6;
    }
    else if (year >= 2200 && year <= 2299) {
        m = 25;
        n = 0;
    }

    a = year % 19;
    b = year % 4;
    c = year % 7;
    d = ((a * 19) + m) % 30;
    e = ((2 * b) + (4 * c) + (6 * d) + n) % 7;

    var dia = d + e;

    if (dia < 10) //Marzo
        return new Date(year, 2, dia + 22);
    else { //Abril

        if (dia == 26)
            dia = 19;
        else if (dia == 25 && d == 28 && e == 6 && a > 10)
            dia = 18;
        else
            dia -= 9;

        return new Date(year, 3, dia);
    }
};

HolidaysHelper.prototype.adicionarFeriado = function(){};
<|endoftext|>


<commit_before>// BusinessProcess data forms (step 1 of Part A) resolution

'use strict';

var memoize                     = require('memoizee/plain')
  , definePropertyGroupsProcess = require('../lib/property-groups-process')
  , defineInitial               = require('./base')
  , defineFormSectionBase       = require('../form-section-base');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess       = defineInitial(db, arguments[1])
	  , PropertyGroupsProcess = definePropertyGroupsProcess(db)
	  , FormSectionBase       = defineFormSectionBase(db);

	BusinessProcess.prototype.defineProperties({
		dataForms: { type: PropertyGroupsProcess, nested: true }
	});

	BusinessProcess.prototype.dataForms.defineProperties({
		// Applicable form sections across all derivedProcesses
		processChainApplicable: {
			type: FormSectionBase,
			multiple: true,
			value: function (_observe) {
				var processes = [this.master], sections = [], taken = Object.create(null);

				// Gather all business processes in chain
				_observe(this.master.derivedBusinessProcesses).forEach(function (process) {
					processes.push(process);
				});
				// Iterate processes in reverse order (latest one should be considered first);
				processes.reverse().forEach(function (process) {
					_observe(process.dataForms.applicable).forEach(function (section, name) {
						if (_observe(section._status) === 0) return;
						if (taken[name]) return;
						taken[name] = true;
						sections.push(section);
					});
				});

				return sections;
			}
		},
		incompleteOnlineDependents: {
			type: FormSectionBase,
			multiple: true,
			value: function (_observe) {
				var result = [];
				this.applicable.forEach(function (section) {
					if (_observe(section._isOnlinePaymentDependent) &&
							_observe(section._status) < 1) {
						result.push(section);
					}
				});

				return result;
			}
		}
	});

	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>#996 cleanup<commit_after>// BusinessProcess data forms (step 1 of Part A) resolution

'use strict';

var memoize                     = require('memoizee/plain')
  , definePropertyGroupsProcess = require('../lib/property-groups-process')
  , defineInitial               = require('./base')
  , defineFormSectionBase       = require('../form-section-base');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess       = defineInitial(db, arguments[1])
	  , PropertyGroupsProcess = definePropertyGroupsProcess(db)
	  , FormSectionBase       = defineFormSectionBase(db);

	BusinessProcess.prototype.defineProperties({
		dataForms: { type: PropertyGroupsProcess, nested: true }
	});

	BusinessProcess.prototype.dataForms.defineProperties({
		// Applicable form sections across all derivedProcesses
		processChainApplicable: {
			type: FormSectionBase,
			multiple: true,
			value: function (_observe) {
				var processes = [this.master], sections = [], taken = Object.create(null);

				// Gather all business processes in chain
				_observe(this.master.derivedBusinessProcesses).forEach(function (process) {
					processes.push(process);
				});
				// Iterate processes in reverse order (latest one should be considered first);
				processes.reverse().forEach(function (process) {
					_observe(process.dataForms.applicable).forEach(function (section, name) {
						if (_observe(section._status) === 0) return;
						if (taken[name]) return;
						taken[name] = true;
						sections.push(section);
					});
				});

				return sections;
			}
		},
		incompleteOnlinePaymentsDependents: {
			type: FormSectionBase,
			multiple: true,
			value: function (_observe) {
				var result = [];
				this.applicable.forEach(function (section) {
					if (_observe(section._isOnlinePaymentDependent) &&
							_observe(section._status) < 1) {
						result.push(section);
					}
				});

				return result;
			}
		}
	});

	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>/* global define describe it expect jasmine */
define([
    '../../../../../../narrative/nbextensions/bulkImportCell/fileTypePanel',
    'common/runtime',
    'bluebird'
], (
    FileTypePanel,
    Runtime,
    Promise
) => {
    'use strict';

    const fileTypes = {
        file1: {
            label: 'A File'
        },
        file2: {
            label: 'Another File'
        },
        file3: {
            label: 'A Third File'
        }
    };
    const header = {
        icon: 'fa fa-times',
        label: 'File Header'
    };
    describe('test the file type panel', () => {

        it('should load and start properly with the right available functions', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            expect(panel.start).toBeDefined();
            expect(panel.stop).toBeDefined();
            expect(panel.updateState).toBeDefined();
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}
            })
                .then(() => {
                    expect(node.innerHTML).toContain(header.label);
                    for (const cat of Object.keys(fileTypes)) {
                        expect(node.innerHTML).toContain(fileTypes[cat].label);
                    }
                });
        });

        it('should respond to an update state signal modifying elements', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}       // start with no state, nothing selected, nothing completed
            })
                .then(() => {
                    let beforeNode = {},
                        beforeIcon = {};
                    for (const cat of Object.keys(fileTypes)) {
                        const elem = node.querySelector(`[data-element="${cat}"]`);
                        beforeNode[cat] = elem.outerHTML;
                        beforeIcon[cat] = elem.querySelector('[data-element="icon"]').outerHTML;
                    }
                    panel.updateState({
                        selected: 'file1'
                    });
                    for (const cat of Object.keys(fileTypes)) {
                        const elem = node.querySelector(`[data-element="${cat}"]`);
                        if (cat === 'file1') {
                            // try to black-box test, so we can just know that it should be
                            // different. (really I shouldn't even use the selector, but
                            // I gotta test something)
                            expect(elem.outerHTML).not.toEqual(beforeNode[cat]);
                        }
                        else {
                            expect(elem.outerHTML).toEqual(beforeNode[cat]);
                        }
                    }
                    panel.updateState({
                        completed: {
                            'file2': true,
                        }
                    });
                    for (const cat of Object.keys(fileTypes)) {
                        const iconElem = node.querySelector(`[data-element="${cat}"] [data-element="icon"]`);
                        if (cat === 'file2') {
                            expect(iconElem.outerHTML).not.toEqual(beforeIcon[cat]);
                        }
                        else {
                            expect(iconElem.outerHTML).toEqual(beforeIcon[cat]);
                        }
                    }
                });
        });

        it('should respond to clicking on an unselected category', () => {
            const clickSpy = jasmine.createSpy('clickSpy');
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: clickSpy
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {
                    selected: 'file1'
                }
            })
                .then(() => {
                    node.querySelector('[data-element="file1"]').click();
                    expect(clickSpy).not.toHaveBeenCalled();
                    node.querySelector('[data-element="file2"]').click();
                    expect(clickSpy).toHaveBeenCalled();
                });
        });

        it('should return a promise from a stop command', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}
            })
                .then(() => {
                    const stopProm = panel.stop();
                    expect(stopProm instanceof Promise).toBeTrue();
                    return stopProm;
                })
                .then(() => {
                    // if we got here, then pass.
                    // This is just here to consume the rest of the promise
                    // so it doesn't dangle.
                });
        });
    });
});
<commit_msg>remove unnecessary 'define' from globals<commit_after>/* global describe it expect jasmine */
define([
    '../../../../../../narrative/nbextensions/bulkImportCell/fileTypePanel',
    'common/runtime',
    'bluebird'
], (
    FileTypePanel,
    Runtime,
    Promise
) => {
    'use strict';

    const fileTypes = {
        file1: {
            label: 'A File'
        },
        file2: {
            label: 'Another File'
        },
        file3: {
            label: 'A Third File'
        }
    };
    const header = {
        icon: 'fa fa-times',
        label: 'File Header'
    };
    describe('test the file type panel', () => {

        it('should load and start properly with the right available functions', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            expect(panel.start).toBeDefined();
            expect(panel.stop).toBeDefined();
            expect(panel.updateState).toBeDefined();
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}
            })
                .then(() => {
                    expect(node.innerHTML).toContain(header.label);
                    for (const cat of Object.keys(fileTypes)) {
                        expect(node.innerHTML).toContain(fileTypes[cat].label);
                    }
                });
        });

        it('should respond to an update state signal modifying elements', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}       // start with no state, nothing selected, nothing completed
            })
                .then(() => {
                    let beforeNode = {},
                        beforeIcon = {};
                    for (const cat of Object.keys(fileTypes)) {
                        const elem = node.querySelector(`[data-element="${cat}"]`);
                        beforeNode[cat] = elem.outerHTML;
                        beforeIcon[cat] = elem.querySelector('[data-element="icon"]').outerHTML;
                    }
                    panel.updateState({
                        selected: 'file1'
                    });
                    for (const cat of Object.keys(fileTypes)) {
                        const elem = node.querySelector(`[data-element="${cat}"]`);
                        if (cat === 'file1') {
                            // try to black-box test, so we can just know that it should be
                            // different. (really I shouldn't even use the selector, but
                            // I gotta test something)
                            expect(elem.outerHTML).not.toEqual(beforeNode[cat]);
                        }
                        else {
                            expect(elem.outerHTML).toEqual(beforeNode[cat]);
                        }
                    }
                    panel.updateState({
                        completed: {
                            'file2': true,
                        }
                    });
                    for (const cat of Object.keys(fileTypes)) {
                        const iconElem = node.querySelector(`[data-element="${cat}"] [data-element="icon"]`);
                        if (cat === 'file2') {
                            expect(iconElem.outerHTML).not.toEqual(beforeIcon[cat]);
                        }
                        else {
                            expect(iconElem.outerHTML).toEqual(beforeIcon[cat]);
                        }
                    }
                });
        });

        it('should respond to clicking on an unselected category', () => {
            const clickSpy = jasmine.createSpy('clickSpy');
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: clickSpy
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {
                    selected: 'file1'
                }
            })
                .then(() => {
                    node.querySelector('[data-element="file1"]').click();
                    expect(clickSpy).not.toHaveBeenCalled();
                    node.querySelector('[data-element="file2"]').click();
                    expect(clickSpy).toHaveBeenCalled();
                });
        });

        it('should return a promise from a stop command', () => {
            let panel = FileTypePanel.make({
                bus: Runtime.make().bus(),
                categories: fileTypes,
                header: header,
                toggleAction: () => {}
            });
            let node = document.createElement('div');
            return panel.start({
                node: node,
                state: {}
            })
                .then(() => {
                    const stopProm = panel.stop();
                    expect(stopProm instanceof Promise).toBeTrue();
                    return stopProm;
                })
                .then(() => {
                    // if we got here, then pass.
                    // This is just here to consume the rest of the promise
                    // so it doesn't dangle.
                });
        });
    });
});
<|endoftext|>


<commit_before>var pathExtra = require('path-extra');
var Pouchdb = require('pouchdb');
var printit = require('printit');
var logger = printit({prefix: 'Cozy Light'});

var platformStatus = 'stopped';

var cozyLight = {

  setStarted: function () {
    platformStatus = 'started';
  },

  setStopped: function () {
    platformStatus = 'stopped';
  },

  getStatus: function () {
    return platformStatus;
  },

  init: function (program) {
    if (program === undefined) { program = {}; }
    var defaultHome = pathExtra.join(pathExtra.homedir(), '.cozy-light');
    var home = program.home || defaultHome;
    var port = program.port || "19104";

    cozyLight.logger.debug('Initializing Cozy Light...');
    cozyLight.npmHelpers.initialWd = process.cwd();
    cozyLight.configHelpers.init(home, port);
    process.chdir(cozyLight.configHelpers.getHomePath());

    cozyLight.logger.debug('Home is ' + cozyLight.configHelpers.getHomePath());
    cozyLight.db = new Pouchdb('cozy');
    cozyLight.pluginHelpers.loadAll(program);

    // Reload apps when file configuration is modified
    cozyLight.configHelpers.watcher.on(function () {
      cozyLight.logger.info('Configuration changed...');
      if (cozyLight.getStatus() === 'started') {
        cozyLight.logger.info('Cozy Light is restarting...');
        cozyLight.actions.restart(program);
      }
    });
  },

  /**
   * Manage properly exit of the process when SIGINT signal is triggered.
   * It asks to every plugin to end properly.
   */
  stop: function (callback) {
    cozyLight.actions.stop(function endProcess (err) {
      if (err) {
        cozyLight.logger.error('Cozy Light was not properly terminated.');
      } else {
        cozyLight.logger.debug('Cozy Light was properly terminated.');
      }

      var doneCallback = function(){
        if (cozyLight.configHelpers.watcher)
          cozyLight.configHelpers.watcher.release();
        cozyLight.nodeHelpers.invoke(callback);
      };

      if (cozyLight.db) {
        try {
          /*eslint-disable */
          if(!cozyLight.db._closed) {
            /*eslint-enable */
            return cozyLight.db.close(doneCallback);
          }
        } catch (err) {
          cozyLight.logger.error(err);
        }
      }
      return cozyLight.nodeHelpers.invoke(doneCallback);
    });
  },

  /**
   * Manage properly exit of the process when SIGINT signal is triggered.
   * It asks to every plugin to end properly.
   */
  exit: function (err) {
    if (!cozyLight.nodeHelpers.hasFreedResources()) {
      cozyLight.logger.info('Has remaining open handles...');
      cozyLight.logger.info('Killing process.');
      /*eslint-disable */
      process.exit(err ? 1 : 0);
      /*eslint-enable */
    }
  },

  logger: logger,
  db: null,
  routes: require('./routes'),
  configHelpers: require('./config'),
  nodeHelpers: require('./helpers/node'),
  npmHelpers: require('./helpers/npm'),
  pluginHelpers: require('./plugins'),
  applicationHelpers: require('./applications'),
  mainAppHelper: require('./main'),
  actions: require('./actions')
};

module.exports = cozyLight;
<commit_msg>let node release references<commit_after>var pathExtra = require('path-extra');
var Pouchdb = require('pouchdb');
var printit = require('printit');
var logger = printit({prefix: 'Cozy Light'});

var platformStatus = 'stopped';

var cozyLight = {

  setStarted: function () {
    platformStatus = 'started';
  },

  setStopped: function () {
    platformStatus = 'stopped';
  },

  getStatus: function () {
    return platformStatus;
  },

  init: function (program) {
    if (program === undefined) { program = {}; }
    var defaultHome = pathExtra.join(pathExtra.homedir(), '.cozy-light');
    var home = program.home || defaultHome;
    var port = program.port || "19104";

    cozyLight.logger.debug('Initializing Cozy Light...');
    cozyLight.npmHelpers.initialWd = process.cwd();
    cozyLight.configHelpers.init(home, port);
    process.chdir(cozyLight.configHelpers.getHomePath());

    cozyLight.logger.debug('Home is ' + cozyLight.configHelpers.getHomePath());
    cozyLight.db = new Pouchdb('cozy');
    cozyLight.pluginHelpers.loadAll(program);

    // Reload apps when file configuration is modified
    cozyLight.configHelpers.watcher.on(function () {
      cozyLight.logger.info('Configuration changed...');
      if (cozyLight.getStatus() === 'started') {
        cozyLight.logger.info('Cozy Light is restarting...');
        process.nextTick(function(){
          cozyLight.actions.restart(program);
        });
      }
    });
  },

  /**
   * Manage properly exit of the process when SIGINT signal is triggered.
   * It asks to every plugin to end properly.
   */
  stop: function (callback) {
    cozyLight.actions.stop(function endProcess (err) {
      if (err) {
        cozyLight.logger.error('Cozy Light was not properly terminated.');
      } else {
        cozyLight.logger.debug('Cozy Light was properly terminated.');
      }

      var doneCallback = function(){
        if (cozyLight.configHelpers.watcher)
          cozyLight.configHelpers.watcher.release();
        cozyLight.nodeHelpers.invoke(callback);
      };

      if (cozyLight.db) {
        try {
          /*eslint-disable */
          if(!cozyLight.db._closed) {
            /*eslint-enable */
            return cozyLight.db.close(doneCallback);
          }
        } catch (err) {
          cozyLight.logger.error(err);
        }
      }
      return cozyLight.nodeHelpers.invoke(doneCallback);
    });
  },

  /**
   * Manage properly exit of the process when SIGINT signal is triggered.
   * It asks to every plugin to end properly.
   */
  exit: function (err) {
    if (!cozyLight.nodeHelpers.hasFreedResources()) {
      cozyLight.logger.info('Has remaining open handles...');
      cozyLight.logger.info('Killing process.');
      /*eslint-disable */
      process.exit(err ? 1 : 0);
      /*eslint-enable */
    }
  },

  logger: logger,
  db: null,
  routes: require('./routes'),
  configHelpers: require('./config'),
  nodeHelpers: require('./helpers/node'),
  npmHelpers: require('./helpers/npm'),
  pluginHelpers: require('./plugins'),
  applicationHelpers: require('./applications'),
  mainAppHelper: require('./main'),
  actions: require('./actions')
};

module.exports = cozyLight;
<|endoftext|>


<commit_before>Package.describe({
  name: 'fermuch:imgix',
  version: '0.0.3_4',
  // Brief, one-line summary of the package.
  summary: 'imgix server/client wrapped for meteor',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/fermuch/meteor-imgix.git',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Npm.depends({
  "imgix-core-js": "0.2.1"
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');

  api.addFiles([
    'imgix.js/dist/imgix.min.js',
    'imgix.js/dist/imgix.jquery.js',
    'loader.js'
  ], 'client');

  api.addFiles(['loader.js'], 'server');

  api.export('imgix', 'client');
  api.export('imgix', 'server');
});

Package.onTest(function(api) {
  api.use('tinytest');
  api.use('fermuch:imgix');
  api.use('coffeescript');
  api.addFiles('imgix-tests.coffee');
});
<commit_msg>version bump<commit_after>Package.describe({
  name: 'fermuch:imgix',
  version: '0.0.3_4',
  // Brief, one-line summary of the package.
  summary: 'imgix server/client wrapped for meteor',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/fermuch/meteor-imgix.git',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Npm.depends({
  "imgix-core-js": "0.2.1"
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');

  api.addFiles([
    'imgix.js/dist/imgix.min.js',
    'imgix.js/dist/imgix.jquery.min.js',
    'loader.js'
  ], 'client');

  api.addFiles(['loader.js'], 'server');

  api.export('imgix', 'client');
  api.export('imgix', 'server');
});

Package.onTest(function(api) {
  api.use('tinytest');
  api.use('fermuch:imgix');
  api.use('coffeescript');
  api.addFiles('imgix-tests.coffee');
});
<|endoftext|>