<commit_before>const buildMiddlewareParameters = require('~/test/unit/helpers/middleware-parameters-builder.js')

const companyMock = require('~/test/unit/data/companies/company-v4.json')
const { setInteractionsDetails } = require('~/src/apps/companies/middleware/interactions')

describe('Interactions middleware', () => {
  describe('#setInteractionsDetails', () => {
    beforeEach(() => {
      this.middlewareParameters = buildMiddlewareParameters({
        company: companyMock,
      })

      setInteractionsDetails(
        this.middlewareParameters.reqMock,
        this.middlewareParameters.resMock,
        this.middlewareParameters.nextSpy,
      )
    })

    it('should set the view', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.view).to.equal('companies/views/interactions')
    })

    it('should set the return link', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.returnLink).to.equal('/companies/a73efeba-8499-11e6-ae22-56b6b6499611/interactions/')
    })

    it('should set the entity name', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.entityName).to.equal('Mercury Ltd')
    })

    it('should set the query', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.query).to.deep.equal({ company_id: 'a73efeba-8499-11e6-ae22-56b6b6499611' })
    })

    it('should set the add flag', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.canAdd).to.equal(true)
    })

    it('should set the breadcrumbs', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.breadcrumbs).to.deep.equal([
        {
          href: `/companies/${companyMock.id}`,
          text: companyMock.name,
        },
        {
          href: `/companies/${companyMock.id}/interactions`,
          text: 'Interactions',
        },
      ])
    })

    it('should call next once', () => {
      expect(this.middlewareParameters.nextSpy).to.be.calledWithExactly()
      expect(this.middlewareParameters.nextSpy).to.be.calledOnce
    })
  })
})
<commit_msg>Tidy company interactions middleware unit tests<commit_after>const buildMiddlewareParameters = require('~/test/unit/helpers/middleware-parameters-builder.js')

const companyMock = require('~/test/unit/data/companies/company-v4.json')
const { setInteractionsDetails } = require('~/src/apps/companies/middleware/interactions')

describe('Interactions middleware', () => {
  describe('#setInteractionsDetails', () => {
    beforeEach(() => {
      this.middlewareParameters = buildMiddlewareParameters({
        company: companyMock,
      })

      setInteractionsDetails(
        this.middlewareParameters.reqMock,
        this.middlewareParameters.resMock,
        this.middlewareParameters.nextSpy,
      )
    })

    it('should set the view', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.view).to.equal('companies/views/interactions')
    })

    it('should set the return link', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.returnLink).to.equal(`/companies/${companyMock.id}/interactions/`)
    })

    it('should set the entity name', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.entityName).to.equal(companyMock.name)
    })

    it('should set the query', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.query).to.deep.equal({ company_id: companyMock.id })
    })

    it('should set the add flag', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.canAdd).to.equal(true)
    })

    it('should set the show company flag', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.showCompany).to.equal(false)
    })

    it('should set the breadcrumbs', () => {
      expect(this.middlewareParameters.resMock.locals.interactions.breadcrumbs).to.deep.equal([
        {
          href: `/companies/${companyMock.id}`,
          text: companyMock.name,
        },
        {
          href: `/companies/${companyMock.id}/interactions`,
          text: 'Interactions',
        },
      ])
    })

    it('should call next once', () => {
      expect(this.middlewareParameters.nextSpy).to.be.calledWithExactly()
      expect(this.middlewareParameters.nextSpy).to.be.calledOnce
    })
  })
})
<|endoftext|>


<commit_before>// Require dependencies
const uuid   = require('uuid');
const events = require('events');

// global vars
let socket = null;

/**
 * Create live model class
 *
 * @extends events
 */
class model extends events {

  /**
   * Construct model class
   *
   * @param {String} type
   * @param {Object} object
   */
  constructor (type, object) {
    // Run super
    super();

    // Set id
    this.__id    = object.id;
    this.__data  = object;
    this.__type  = type;
    this.__queue = [];

    // Bind methods
    this.get     = this.get.bind(this);
    this.build   = this.build.bind(this);
    this.listen  = this.listen.bind(this);
    this.refresh = this.refresh.bind(this);
    this.destroy = this.destroy.bind(this);

    // Bind private methods
    this._update  = this._update.bind(this);
    this._connect = this._connect.bind(this);

    // Build
    this.building = this.build();
  }

  /**
   * Returns data key
   *
   * @param  {String} key
   *
   * @return {*}
   */
  get (key) {
    // Check key
    if (!key || !key.length) return this.__data;

    // Return this key
    return this.__data[key];
  }

  /**
   * Builds this
   */
  async build () {
    // Check if window
    if (typeof window !== 'undefined') socket = require('socket/public/js/bootstrap');

    // Listen
    await this.listen();
  }

  /**
   * Listens to model by id
   *
   * @return {Promise}
   */
  async destroy () {
    // Await building
    await this.building;

    // Check listening
    await Promise.all(this.__queue);

    // Check loading
    if (!socket || !this.__isListening) return;

    // Create new promise
    let promise = new Promise(async (resolve) => {
      // Call eden
      await socket.call('live.deafen.' + this.__type, this.__id, this.__uuid);

      // Set listen
      this.__isListening = false;

      // Add on event
      socket.off('live.update.' + this.__type + '.' + this.__id, this._update);

      // Listen to connect again
      socket.off('connect', this._connect);

      // Resolve
      resolve();
    });

    // Add to queue
    this.__queue.push(promise);

    // Return await deafening
    return await promise;
  }

  /**
   * Refreshes this
   */
  async refresh () {
    // Await building
    await this.building;

    // Check loading
    if (!socket) return;

    // Call eden
    let object = await socket.call('live.refresh.' + this.__type, this.__id);

    // Run update
    this._update(object);
  }

  /**
   * Listens to model by id
   *
   * @return {Promise}
   */
  async listen () {
    // Await building
    await this.building;

    // Check listening
    await Promise.all(this.__queue);

    // Check loading
    if (!socket || this.__isListening) return;

    // Set uuid
    if (!this.__uuid) this.__uuid = uuid();

    // Create new promise
    let promise = new Promise(async (resolve) => {
      // Call eden
      await socket.call('live.listen.' + this.__type, this.__id, this.__uuid);

      // Set listen
      this.__isListening = true;

      // Add on event
      socket.on('live.update.' + this.__type + '.' + this.__id, this._update);

      // Listen to connect again
      socket.on('connect', this._connect);

      // Resolve
      resolve();
    });

    // Add to queue
    this.__queue.push(promise);

    // Return listening promise
    return await promise;
  }

  /**
   * On update
   *
   * @param  {Object} object
   */
  _update (object) {
    // Update details
    for (let key in object) {
      // Check differences
      if (this.__data[key] !== object[key]) {
        // Listen to object key
        this.__data[key] = object[key];

        // Emit event
        this.emit(key, object[key]);
      }
    }

    // Emit update
    this.emit('update');
  }

  /**
   * On socket reconnect
   */
  _connect () {
    // Reconnected
    if (this.__isListening) {
      // Call live listen again
      socket.call('live.listen.' + this.__type, this.__id, this.__uuid);
    }
  }
}

/**
 * Export live model class
 *
 * @type {model}
 */
exports = module.exports = model;
<commit_msg>use dotprop for live models<commit_after>// Require dependencies
const uuid    = require('uuid');
const events  = require('events');
const dotProp = require('dot-prop-immutable');

// global vars
let socket = null;

/**
 * Create live model class
 *
 * @extends events
 */
class model extends events {

  /**
   * Construct model class
   *
   * @param {String} type
   * @param {Object} object
   */
  constructor (type, object) {
    // Run super
    super();

    // Set id
    this.__id    = object.id;
    this.__data  = object;
    this.__type  = type;
    this.__queue = [];

    // Bind methods
    this.get     = this.get.bind(this);
    this.build   = this.build.bind(this);
    this.listen  = this.listen.bind(this);
    this.refresh = this.refresh.bind(this);
    this.destroy = this.destroy.bind(this);

    // Bind private methods
    this._update  = this._update.bind(this);
    this._connect = this._connect.bind(this);

    // Build
    this.building = this.build();
  }

  /**
   * Returns data key
   *
   * @param  {String} key
   *
   * @return {*}
   */
  get (key) {
    // Check key
    if (!key || !key.length) return this.__data;

    // Return this key
    return dotProp.get(this.__data, key);
  }

  /**
   * Builds this
   */
  async build () {
    // Check if window
    if (typeof window !== 'undefined') socket = require('socket/public/js/bootstrap');

    // Listen
    await this.listen();
  }

  /**
   * Listens to model by id
   *
   * @return {Promise}
   */
  async destroy () {
    // Await building
    await this.building;

    // Check listening
    await Promise.all(this.__queue);

    // Check loading
    if (!socket || !this.__isListening) return;

    // Create new promise
    let promise = new Promise(async (resolve) => {
      // Call eden
      await socket.call('live.deafen.' + this.__type, this.__id, this.__uuid);

      // Set listen
      this.__isListening = false;

      // Add on event
      socket.off('live.update.' + this.__type + '.' + this.__id, this._update);

      // Listen to connect again
      socket.off('connect', this._connect);

      // Resolve
      resolve();
    });

    // Add to queue
    this.__queue.push(promise);

    // Return await deafening
    return await promise;
  }

  /**
   * Refreshes this
   */
  async refresh () {
    // Await building
    await this.building;

    // Check loading
    if (!socket) return;

    // Call eden
    let object = await socket.call('live.refresh.' + this.__type, this.__id);

    // Run update
    this._update(object);
  }

  /**
   * Listens to model by id
   *
   * @return {Promise}
   */
  async listen () {
    // Await building
    await this.building;

    // Check listening
    await Promise.all(this.__queue);

    // Check loading
    if (!socket || this.__isListening) return;

    // Set uuid
    if (!this.__uuid) this.__uuid = uuid();

    // Create new promise
    let promise = new Promise(async (resolve) => {
      // Call eden
      await socket.call('live.listen.' + this.__type, this.__id, this.__uuid);

      // Set listen
      this.__isListening = true;

      // Add on event
      socket.on('live.update.' + this.__type + '.' + this.__id, this._update);

      // Listen to connect again
      socket.on('connect', this._connect);

      // Resolve
      resolve();
    });

    // Add to queue
    this.__queue.push(promise);

    // Return listening promise
    return await promise;
  }

  /**
   * On update
   *
   * @param  {Object} object
   */
  _update (object) {
    // Update details
    for (let key in object) {
      // Check differences
      if (this.__data[key] !== object[key]) {
        // Listen to object key
        this.__data[key] = object[key];

        // Emit event
        this.emit(key, object[key]);
      }
    }

    // Emit update
    this.emit('update');
  }

  /**
   * On socket reconnect
   */
  _connect () {
    // Reconnected
    if (this.__isListening) {
      // Call live listen again
      socket.call('live.listen.' + this.__type, this.__id, this.__uuid);
    }
  }
}

/**
 * Export live model class
 *
 * @type {model}
 */
exports = module.exports = model;
<|endoftext|>


<commit_before><commit_msg>Rename lineEnds() to lineEndPoints()<commit_after><|endoftext|>


<commit_before>'use strict';

const process = require('process');
const { promisify } = require('util');

const { watch, series, parallel } = require('gulp');

const FILES = require('../files');

// Returns a watch task
// E.g. with `tasks` `{ FORMAT: format }`, the `format` task will be fired
// everytime `FILES.FORMAT` is changed.
// If specified, `initialTask` is fired first
const getWatchTask = function (tasks, initialTask) {
  const watchTask = getWatchTasks(tasks);

  if (initialTask === undefined) { return watchTask; }

  // Runs the task before watching
  // Using `ignoreInitial` chokidar option does not work because the task would
  // be marked as complete before the initial run.
  return series(initialTask, watchTask);
};

const getWatchTasks = tasks => function watchTasks () {
  const promises = Object.entries(tasks)
    .map(([type, task]) => watchByType({ type, task }));
  return Promise.all(promises);
};

const watchByType = async function ({ type, task }) {
  const taskA = Array.isArray(task) ? parallel(task) : task;
  const watcher = watch(FILES[type], taskA);

  // Otherwise the task will hang when fired through npm scripts
  process.on('SIGINT', watcher.close.bind(watcher));

  // Wait for watching to be setup to mark the `watch` task as complete
  await promisify(watcher.on.bind(watcher))('ready');
};

module.exports = {
  getWatchTask,
};
<commit_msg>Fix Nodemon + Yarn integration problem<commit_after>'use strict';

const process = require('process');
const { promisify } = require('util');

const { watch, series, parallel } = require('gulp');

const FILES = require('../files');

// Returns a watch task
// E.g. with `tasks` `{ FORMAT: format }`, the `format` task will be fired
// everytime `FILES.FORMAT` is changed.
// If specified, `initialTask` is fired first
const getWatchTask = function (tasks, initialTask) {
  const watchTask = getWatchTasks(tasks);

  if (initialTask === undefined) { return watchTask; }

  // Runs the task before watching
  // Using `ignoreInitial` chokidar option does not work because the task would
  // be marked as complete before the initial run.
  return series(initialTask, watchTask);
};

const getWatchTasks = tasks => function watchTasks () {
  const promises = Object.entries(tasks)
    .map(([type, task]) => watchByType({ type, task }));
  return Promise.all(promises);
};

const watchByType = async function ({ type, task }) {
  const taskA = Array.isArray(task) ? parallel(task) : task;
  const watcher = watch(FILES[type], taskA);

  // Wait for watching to be setup to mark the `watch` task as complete
  await promisify(watcher.on.bind(watcher))('ready');
};

module.exports = {
  getWatchTask,
};
<|endoftext|>


<commit_before>/**
 * WordPress dependencies
 */
import { Component, Fragment } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * External dependencies
 */
import ProgressBar from 'GoogleComponents/progress-bar';
import PropTypes from 'prop-types';

/**
 * Internal dependencies
 */
import Warning from '../notifications/warning';

/**
 * Internal dependencies
 */
import { getExistingTag } from '../../util';
import data, { TYPE_CORE } from '../data';
import Link from '../link';

export const tagMatchers = [
	/<meta name="googlesitekit-setup" content="([a-z0-9-]+)"/,
];

const checks = [
	async () => {
		const { hostname } = window.location;

		if ( hostname === 'localhost' || hostname.match( /\.(example|invalid|localhost|test)$/ ) ) {
			throw 'invalid_hostname';
		}
	},
	async () => {
		const { token } = await data.set( TYPE_CORE, 'site', 'setup-tag' );

		const scrapedTag = await getExistingTag( 'setup' ).catch( () => {
			throw 'tag_fetch_failed';
		} );

		if ( token !== scrapedTag ) {
			throw 'setup_token_mismatch';
		}
	},
];

export default class CompatibilityChecks extends Component {
	constructor( props ) {
		const { isSiteKitConnected } = window.googlesitekit.setup;
		super( props );
		this.state = {
			complete: isSiteKitConnected,
			error: null,
			helperPlugin: {},
		};
	}

	async componentDidMount() {
		if ( this.state.complete ) {
			return;
		}
		try {
			for ( const testCallback of checks ) {
				await testCallback();
			}
		} catch ( error ) {
			const helperPlugin = await data.get( TYPE_CORE, 'site', 'helper-plugin' );
			this.setState( { error, helperPlugin } );
		}

		this.setState( { complete: true } );
	}

	helperCTA() {
		const { installed, active, installURL, activateURL, configureURL } = this.state.helperPlugin;

		if ( ! installed && installURL ) {
			return {
				labelHTML: __( 'Install the helper plugin', 'google-site-kit' ),
				href: installURL,
				external: false,
			};
		}
		if ( installed && ! active && activateURL ) {
			return {
				labelHTML: __( 'Activate the helper plugin', 'google-site-kit' ),
				href: activateURL,
				external: false,
			};
		}
		if ( installed && active && configureURL ) {
			return {
				labelHTML: __( 'Configure the helper plugin', 'google-site-kit' ),
				href: configureURL,
				external: false,
			};
		}
		return {
			labelHTML: __( 'Learn how to install and use the helper plugin', 'google-site-kit' ),
			href: 'https://sitekit.withgoogle.com/documentation/using-site-kit-on-a-staging-environment/',
			external: true,
		};
	}

	renderError( error ) {
		const { installed } = this.state.helperPlugin;
		const { labelHTML, href, external } = this.helperCTA();

		switch ( error ) {
			case 'invalid_hostname':
			case 'tag_fetch_failed':
				return <Fragment>
					{ ! installed && __( 'Looks like this may be a staging environment. If so, you’ll need to install a helper plugin and verify your production site in Search Console.', 'google-site-kit' ) }
					{ installed && __( 'Looks like this may be a staging environment and you already have the helper plugin. Before you can use Site Kit, please make sure you’ve provided the necessary credentials in the Authentication section and verified your production site in Search Console.', 'google-site-kit' ) }
					{ ' ' }
					<Link
						href={ href }
						dangerouslySetInnerHTML={ { __html: labelHTML } }
						external={ external }
						inherit
					/>
				</Fragment>;
			case 'setup_token_mismatch':
				return __( 'Looks like you may be using a caching plugin which could interfere with setup. Please deactivate any caching plugins before setting up Site Kit. You may reactivate them once setup has been completed.', 'google-site-kit' );
		}
	}

	render() {
		const { complete, error } = this.state;
		const { children, ...restProps } = this.props;

		let CTAFeedback;
		let inProgressFeedback;

		if ( error ) {
			CTAFeedback = <Fragment>

						<Warning />

							{ __( 'Your site may not be ready for Site Kit', 'google-site-kit' ) }

{ this.renderError( error ) }

			</Fragment>;
		}

		if ( ! complete ) {
			inProgressFeedback =

				{ __( 'Checking Compatibility...', 'google-site-kit' ) }
				<ProgressBar small compress />

;
		}

		return children( {
			restProps,
			complete,
			error,
			inProgressFeedback,
			CTAFeedback,
		} );
	}
}

CompatibilityChecks.propTypes = {
	children: PropTypes.func.isRequired,
};
<commit_msg>Add loopback IP as a non-public hostname to check for.<commit_after>/**
 * WordPress dependencies
 */
import { Component, Fragment } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * External dependencies
 */
import ProgressBar from 'GoogleComponents/progress-bar';
import PropTypes from 'prop-types';

/**
 * Internal dependencies
 */
import Warning from '../notifications/warning';

/**
 * Internal dependencies
 */
import { getExistingTag } from '../../util';
import data, { TYPE_CORE } from '../data';
import Link from '../link';

export const tagMatchers = [
	/<meta name="googlesitekit-setup" content="([a-z0-9-]+)"/,
];

const checks = [
	async () => {
		const { hostname } = window.location;

		if ( [ 'localhost', '127.0.0.1' ].includes( hostname ) || hostname.match( /\.(example|invalid|localhost|test)$/ ) ) {
			throw 'invalid_hostname';
		}
	},
	async () => {
		const { token } = await data.set( TYPE_CORE, 'site', 'setup-tag' );

		const scrapedTag = await getExistingTag( 'setup' ).catch( () => {
			throw 'tag_fetch_failed';
		} );

		if ( token !== scrapedTag ) {
			throw 'setup_token_mismatch';
		}
	},
];

export default class CompatibilityChecks extends Component {
	constructor( props ) {
		const { isSiteKitConnected } = window.googlesitekit.setup;
		super( props );
		this.state = {
			complete: isSiteKitConnected,
			error: null,
			helperPlugin: {},
		};
	}

	async componentDidMount() {
		if ( this.state.complete ) {
			return;
		}
		try {
			for ( const testCallback of checks ) {
				await testCallback();
			}
		} catch ( error ) {
			const helperPlugin = await data.get( TYPE_CORE, 'site', 'helper-plugin' );
			this.setState( { error, helperPlugin } );
		}

		this.setState( { complete: true } );
	}

	helperCTA() {
		const { installed, active, installURL, activateURL, configureURL } = this.state.helperPlugin;

		if ( ! installed && installURL ) {
			return {
				labelHTML: __( 'Install the helper plugin', 'google-site-kit' ),
				href: installURL,
				external: false,
			};
		}
		if ( installed && ! active && activateURL ) {
			return {
				labelHTML: __( 'Activate the helper plugin', 'google-site-kit' ),
				href: activateURL,
				external: false,
			};
		}
		if ( installed && active && configureURL ) {
			return {
				labelHTML: __( 'Configure the helper plugin', 'google-site-kit' ),
				href: configureURL,
				external: false,
			};
		}
		return {
			labelHTML: __( 'Learn how to install and use the helper plugin', 'google-site-kit' ),
			href: 'https://sitekit.withgoogle.com/documentation/using-site-kit-on-a-staging-environment/',
			external: true,
		};
	}

	renderError( error ) {
		const { installed } = this.state.helperPlugin;
		const { labelHTML, href, external } = this.helperCTA();

		switch ( error ) {
			case 'invalid_hostname':
			case 'tag_fetch_failed':
				return <Fragment>
					{ ! installed && __( 'Looks like this may be a staging environment. If so, you’ll need to install a helper plugin and verify your production site in Search Console.', 'google-site-kit' ) }
					{ installed && __( 'Looks like this may be a staging environment and you already have the helper plugin. Before you can use Site Kit, please make sure you’ve provided the necessary credentials in the Authentication section and verified your production site in Search Console.', 'google-site-kit' ) }
					{ ' ' }
					<Link
						href={ href }
						dangerouslySetInnerHTML={ { __html: labelHTML } }
						external={ external }
						inherit
					/>
				</Fragment>;
			case 'setup_token_mismatch':
				return __( 'Looks like you may be using a caching plugin which could interfere with setup. Please deactivate any caching plugins before setting up Site Kit. You may reactivate them once setup has been completed.', 'google-site-kit' );
		}
	}

	render() {
		const { complete, error } = this.state;
		const { children, ...restProps } = this.props;

		let CTAFeedback;
		let inProgressFeedback;

		if ( error ) {
			CTAFeedback = <Fragment>

						<Warning />

							{ __( 'Your site may not be ready for Site Kit', 'google-site-kit' ) }

{ this.renderError( error ) }

			</Fragment>;
		}

		if ( ! complete ) {
			inProgressFeedback =

				{ __( 'Checking Compatibility...', 'google-site-kit' ) }
				<ProgressBar small compress />

;
		}

		return children( {
			restProps,
			complete,
			error,
			inProgressFeedback,
			CTAFeedback,
		} );
	}
}

CompatibilityChecks.propTypes = {
	children: PropTypes.func.isRequired,
};
<|endoftext|>


<commit_before>
noosfero.tinymce = {

  defaultOptions: {
    mode: "textareas", //necessary for compat3x
    theme: "modern",
    relative_urls: false,
    remove_script_host: false,
    extended_valid_elements: "applet[style|archive|codebase|code|height|width],comment,iframe[src|style|allowtransparency|frameborder|width|height|scrolling],embed[title|src|type|height|width],audio[controls|autoplay],video[controls|autoplay],source[src|type]",
    entity_encoding: 'raw',
    setup: function(editor) {
      tinymce_macros_setup(editor)
    },
  },

  init: function(_options) {
    var options = jQuery.extend({}, this.defaultOptions, _options)
    tinymce.init(options);
    jQuery('.mceEditor').tinymce(options);

    jQuery('form').bind('form-pre-serialize', function(e) {
      for (var ed in tinymce.editors)
        ed.save()
    });

  },
};
<commit_msg>Fix save again<commit_after>
noosfero.tinymce = {

  defaultOptions: {
    theme: "modern",
    relative_urls: false,
    remove_script_host: false,
    extended_valid_elements: "applet[style|archive|codebase|code|height|width],comment,iframe[src|style|allowtransparency|frameborder|width|height|scrolling],embed[title|src|type|height|width],audio[controls|autoplay],video[controls|autoplay],source[src|type]",
    entity_encoding: 'raw',
    setup: function(editor) {
      tinymce_macros_setup(editor)
    },
  },

  init: function(_options) {
    var options = jQuery.extend({}, this.defaultOptions, _options)
    jQuery('.mceEditor').tinymce(options);
  },
};
<|endoftext|>


<commit_before>// @flow

import {
  parseHtml,
  cssSelect,
  getTrimmedTextWithSpaces as getText,
} from '../../../lib/html'
import type {FullJobType} from './types'

const booleanKeys = [
  'Position available during term',
  'Position available during break',
]

const paragraphicalKeys = ['Description']

export async function fetchJob(url: string): Promise<FullJobType> {
  const page = await fetch(url).then(r => r.text())
  const dom = parseHtml(page)

  const jobs = cssSelect('#jobs', dom)
  const title = cssSelect('> h3', jobs)[0]
  const details = cssSelect('> ul:first-of-type > li', jobs)

  let titleText = getText(title).trim()
  const offCampus = /^Off Campus/.test(titleText)
  if (offCampus) {
    titleText = titleText.replace(/^Off Campus: +/, '')
  }

  const detailMap: Map<string, string> = details.reduce((coll, listEl) => {
    let [key, ...value] = listEl.children
    key = getText(key).replace(/:$/, '')

    if (booleanKeys.includes(key)) {
      value = true
    } else if (paragraphicalKeys.includes(key)) {
      value = cssSelect('p', listEl).map(getText).join('\n\n').trim()
    } else {
      value = getText(value).trim()
    }

    coll.set(key, value)

    return coll
  }, new Map())

  return {
    title: titleText,
    offCampus: offCampus,
    department: detailMap.get('Department or Office'),
    dateOpen: detailMap.get('Date Open') || 'Unknown',
    duringTerm: Boolean(detailMap.get('Position available during term')),
    duringBreak: Boolean(detailMap.get('Position available during break')),
    description: detailMap.get('Description') || '',
  }
}
<commit_msg>make job urls https<commit_after>// @flow

import {
  parseHtml,
  cssSelect,
  getTrimmedTextWithSpaces as getText,
} from '../../../lib/html'
import type {FullJobType} from './types'

const booleanKeys = [
  'Position available during term',
  'Position available during break',
]

const paragraphicalKeys = ['Description']

export async function fetchJob(url: string): Promise<FullJobType> {
  url = url.replace(/^http:/, 'https:')
  const page = await fetch(url).then(r => r.text())
  const dom = parseHtml(page)

  const jobs = cssSelect('#jobs', dom)
  const title = cssSelect('> h3', jobs)[0]
  const details = cssSelect('> ul:first-of-type > li', jobs)

  let titleText = getText(title).trim()
  const offCampus = /^Off Campus/.test(titleText)
  if (offCampus) {
    titleText = titleText.replace(/^Off Campus: +/, '')
  }

  const detailMap: Map<string, string> = details.reduce((coll, listEl) => {
    let [key, ...value] = listEl.children
    key = getText(key).replace(/:$/, '')

    if (booleanKeys.includes(key)) {
      value = true
    } else if (paragraphicalKeys.includes(key)) {
      value = cssSelect('p', listEl).map(getText).join('\n\n').trim()
    } else {
      value = getText(value).trim()
    }

    coll.set(key, value)

    return coll
  }, new Map())

  return {
    title: titleText,
    offCampus: offCampus,
    department: detailMap.get('Department or Office'),
    dateOpen: detailMap.get('Date Open') || 'Unknown',
    duringTerm: Boolean(detailMap.get('Position available during term')),
    duringBreak: Boolean(detailMap.get('Position available during break')),
    description: detailMap.get('Description') || '',
  }
}
<|endoftext|>


<commit_before>steal('jquery', './core.js', 'syn', function($, FuncUnit, Syn) {
	window.Syn = Syn;
	/**
	 * @add FuncUnit
	 */
	var clicks = [
	// methods
	/**
     *
	 * @function FuncUnit.prototype.click .click()
     * @parent actions
     * @signature `click(options [,success])`
     *
	 * Clicks an element.  This uses [Syn.prototype.click] to issue a:
	 *

	 *
mousedown

	 *
focus - if the element is focusable

	 *
mouseup

	 *
click

	 *

	 * If no clientX/Y or pageX/Y is provided as options, the click happens at the
	 * center of the element.
	 *
For a right click or double click use [FuncUnit.prototype.rightClick] or
	 *   [FuncUnit.prototype.dblclick].

	 *
Example

	 * @codestart
	 * //clicks the bar element
	 *F("#bar").click()
	 * @codeend
	 * @param {Object} [options] options to pass to the click event.  Typically, this is clientX/Y or pageX/Y:
	 * @codestart
	 * $('#foo').click({pageX: 200, pageY: 100});
	 * @codeend
	 * You can pass it any of the serializable parameters you'd send to
	 * [http://developer.mozilla.org/en/DOM/event.initMouseEvent initMouseEvent], but command keys are
	 * controlled by [FuncUnit.prototype.type].
     *
	 * @param {Function} [success] a callback that runs after the click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'click',
	/**
     * @function FuncUnit.prototype.dblclick .dblclick()
     * @parent actions
     * @signature `dblclick(options [,success])`
     *
	 * Double clicks an element by [FuncUnit.prototype.click clicking] it twice and triggering a dblclick event.
	 * @param {Object} options options to add to the mouse events.  This works
	 * the same as [FuncUnit.prototype.click]'s options.
	 * @param {Function} [success] a callback that runs after the double click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'dblclick',
	/**
     * @function FuncUnit.prototype.rightClick .rightClick()
     * @parent actions
     * @signature `rightClick(options [,success])`
	 * Right clicks an element.  This typically results in a contextmenu event for browsers that
	 * support it.
	 * @param {Object} options options to add to the mouse events.  This works
	 * the same as [FuncUnit.prototype.click]'s options.
	 * @param {Function} [success] a callback that runs after the click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'rightClick'],
		makeClick = function(name){
			FuncUnit.prototype[name] = function(options, success){
				this._addExists();
				if(typeof options == 'function'){
					success = options;
					options = {};
				}
				var selector = this.selector;
				FuncUnit.add({
					method: function(success, error){
						options = options || {};
						Syn("_" + name, options, this.bind[0],success);
					},
					success: success,
					error: "Could not " + name + " '" + this.selector+"'",
					bind: this,
					type: "action"
				});
				return this;
			}
		}

	for(var i=0; i < clicks.length; i++){
		makeClick(clicks[i])
	}

	$.extend(FuncUnit.prototype, {
		// perform check even if last queued item is a wait beacuse certain waits don't guarantee the element is visible (like text)
		_addExists: function(){
			this.exists(false);
		},
		/**
         * @function FuncUnit.prototype.type .type()
         * @parent actions
         * @signature `type(text [,success])`
         *
		 * Types text into an element.  This makes use of [Syn.type] and works in
		 * a very similar way.
		 *
Quick Examples

		 * @codestart
		 * //types hello world
		 *F('#bar').type('hello world')
		 *
		 * //submits a form by typing \r
		 *F("input[name=age]").type("27\r")
		 *
		 * //types FuncUnit, then deletes the Unit
		 *F('#foo').type("FuncUnit\b\b\b\b")
		 *
		 * //types JavaScriptMVC, then removes the MVC
		 *F('#zar').type("JavaScriptMVC[left][left][left]"+
		 *                      "[delete][delete][delete]")
		 *
		 * //types JavaScriptMVC, then selects the MVC and
		 * //deletes it
		 *F('#zar').type("JavaScriptMVC[shift]"+
		 *                "[left][left][left]"+
		 *                "[shift-up][delete]")
		 * @codeend
		 *
		 *
Characters

		 *
		 * For a list of the characters you can type, check [Syn.keycodes].
		 *
		 * @param {String} text the text you want to type
		 * @param {Function} [success] a callback that is run after typing, but before the next action.
		 * @return {FuncUnit} returns the funcUnit object for chaining.
		 */
		type: function( text, success ) {
			this._addExists();
			// when you type in something you have to click on it first
			this.click();
			var selector = this.selector;
			// type("") is a shortcut for clearing out a text input
			if(text === ""){
				text = "[ctrl]a[ctrl-up]\b"
			}
			FuncUnit.add({
				method : function(success, error){
					Syn("_type", text, this.bind[0], success);

				},
				success : success,
				error : "Could not type " + text + " into " + this.selector,
				bind : this,
				type: "action"
			});
			return this;
		},
    // TODO (DL) this needs to be deprecated this advertises .trigger() functionality
    // but expects that the target page will have jQuery which could not be the case.
		trigger: function(evName, success){
			this._addExists();
			FuncUnit.add({
				method : function(success, error){
					// need to use the page's jquery to trigger events
          if(!FuncUnit.win.jQuery) {
            throw 'Can not trigger custom event, no jQuery found on target page.';
          }
					FuncUnit.win.jQuery(this.bind.selector).trigger(evName)
					success()
				},
				success : success,
				error : "Could not trigger " + evName,
				bind : this,
				type: "action"
			});
			return this;
		},
		/**
         * @function FuncUnit.prototype.drag .drag()
         * @parent actions
         * @signature `drag(options [,success])`
		 * Drags an element into another element or coordinates.
		 * This takes the same paramameters as [Syn.prototype.move move].
		 * @param {String|Object} options A selector or coordinates describing the motion of the drag.
		 *
Options as a Selector

		 * Passing a string selector to drag the mouse.  The drag runs to the center of the element
		 * matched by the selector.  The following drags from the center of #foo to the center of #bar.
		 * @codestart
		 *F('#foo').drag('#bar')
		 * @codeend
		 *
Options as Coordinates

		 * You can pass in coordinates as clientX and clientY:
		 * @codestart
		 *F('#foo').drag('100x200')
		 * @codeend
		 * Or as pageX and pageY
		 * @codestart
		 *F('#foo').drag('100X200')
		 * @codeend
		 * Or relative to the start position
		 *F('#foo').drag('+10 +20')
		 *
Options as an Object

		 * You can configure the duration, start, and end point of a drag by passing in a json object.
		 * @codestart
		 * //drags from 0x0 to 100x100 in 2 seconds
		 *F('#foo').drag({
		 *   from: "0x0",
		 *   to: "100x100",
		 *   duration: 2000
		 * })
		 * @codeend
		 * @param {Function} [success] a callback that runs after the drag, but before the next action.
		 * @return {funcUnit} returns the funcunit object for chaining.
		 */
		drag: function( options, success ) {
			this._addExists();
			if(typeof options == 'string'){
				options = {to: options}
			}
			options.from = this.selector;

			var selector = this.selector;
			FuncUnit.add({
				method: function(success, error){
					Syn("_drag", options, this.bind[0],success);
				},
				success: success,
				error: "Could not drag " + this.selector,
				bind: this,
				type: "action"
			})
			return this;
		},
		/**
         * @function FuncUnit.prototype.move .move()
         * @parent actions
         * @signature `move(options [,success])`
		 * Moves an element into another element or coordinates.  This will trigger mouseover
		 * mouseouts accordingly.
		 * This takes the same paramameters as [Syn.prototype.move move].
		 * @param {String|Object} options A selector or coordinates describing the motion of the move.
		 *
Options as a Selector

		 * Passing a string selector to move the mouse.  The move runs to the center of the element
		 * matched by the selector.  The following moves from the center of #foo to the center of #bar.
		 * @codestart
		 *F('#foo').move('#bar')
		 * @codeend
		 *
Options as Coordinates

		 * You can pass in coordinates as clientX and clientY:
		 * @codestart
		 *F('#foo').move('100x200')
		 * @codeend
		 * Or as pageX and pageY
		 * @codestart
		 *F('#foo').move('100X200')
		 * @codeend
		 * Or relative to the start position
		 *F('#foo').move('+10 +20')
		 *
Options as an Object

		 * You can configure the duration, start, and end point of a move by passing in a json object.
		 * @codestart
		 * //drags from 0x0 to 100x100 in 2 seconds
		 *F('#foo').move({
		 *   from: "0x0",
		 *   to: "100x100",
		 *   duration: 2000
		 * })
		 * @codeend
		 * @param {Function} [success] a callback that runs after the drag, but before the next action.
		 * @return {funcUnit} returns the funcunit object for chaining.
		 */
		move: function( options, success ) {
			this._addExists();
			if(typeof options == 'string'){
				options = {to: options}
			}
			options.from = this.selector;

			var selector = this.selector;
			FuncUnit.add({
				method: function(success, error){
					Syn("_move", options, this.bind[0], success);
				},
				success: success,
				error: "Could not move " + this.selector,
				bind: this,
				type: "action"
			});
			return this;
		},
		/**
         * @function FuncUnit.prototype.scroll .scroll()
         * @parent actions
         * @signature `scroll(direction, amount, success)`
		 * Scrolls an element in a particular direction by setting the scrollTop or srollLeft.
		 * @param {String} direction "left" or "top"
		 * @param {Number} amount number of pixels to scroll
		 * @param {Function} success
		 */
		scroll: function( direction, amount, success ) {
			this._addExists();
			var selector = this.selector,
				direction;
			if (direction == "left" || direction == "right") {
				direction = "Left";
			} else if (direction == "top" || direction == "bottom") {
				direction = "Top";
			}
			FuncUnit.add({
				method: function(success, error){
					this.bind.each(function(i, el){
						this["scroll" + direction] = amount;
					})
					success();
				},
				success: success,
				error: "Could not scroll " + this.selector,
				bind: this,
				type: "action"
			});
			return this;
		}
	})
	return FuncUnit;
});
<commit_msg>Fix typo in docs for scroll action.<commit_after>steal('jquery', './core.js', 'syn', function($, FuncUnit, Syn) {
	window.Syn = Syn;
	/**
	 * @add FuncUnit
	 */
	var clicks = [
	// methods
	/**
     *
	 * @function FuncUnit.prototype.click .click()
     * @parent actions
     * @signature `click(options [,success])`
     *
	 * Clicks an element.  This uses [Syn.prototype.click] to issue a:
	 *

	 *
mousedown

	 *
focus - if the element is focusable

	 *
mouseup

	 *
click

	 *

	 * If no clientX/Y or pageX/Y is provided as options, the click happens at the
	 * center of the element.
	 *
For a right click or double click use [FuncUnit.prototype.rightClick] or
	 *   [FuncUnit.prototype.dblclick].

	 *
Example

	 * @codestart
	 * //clicks the bar element
	 *F("#bar").click()
	 * @codeend
	 * @param {Object} [options] options to pass to the click event.  Typically, this is clientX/Y or pageX/Y:
	 * @codestart
	 * $('#foo').click({pageX: 200, pageY: 100});
	 * @codeend
	 * You can pass it any of the serializable parameters you'd send to
	 * [http://developer.mozilla.org/en/DOM/event.initMouseEvent initMouseEvent], but command keys are
	 * controlled by [FuncUnit.prototype.type].
     *
	 * @param {Function} [success] a callback that runs after the click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'click',
	/**
     * @function FuncUnit.prototype.dblclick .dblclick()
     * @parent actions
     * @signature `dblclick(options [,success])`
     *
	 * Double clicks an element by [FuncUnit.prototype.click clicking] it twice and triggering a dblclick event.
	 * @param {Object} options options to add to the mouse events.  This works
	 * the same as [FuncUnit.prototype.click]'s options.
	 * @param {Function} [success] a callback that runs after the double click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'dblclick',
	/**
     * @function FuncUnit.prototype.rightClick .rightClick()
     * @parent actions
     * @signature `rightClick(options [,success])`
	 * Right clicks an element.  This typically results in a contextmenu event for browsers that
	 * support it.
	 * @param {Object} options options to add to the mouse events.  This works
	 * the same as [FuncUnit.prototype.click]'s options.
	 * @param {Function} [success] a callback that runs after the click, but before the next action.
	 * @return {funcUnit} returns the funcunit object for chaining.
	 */
	'rightClick'],
		makeClick = function(name){
			FuncUnit.prototype[name] = function(options, success){
				this._addExists();
				if(typeof options == 'function'){
					success = options;
					options = {};
				}
				var selector = this.selector;
				FuncUnit.add({
					method: function(success, error){
						options = options || {};
						Syn("_" + name, options, this.bind[0],success);
					},
					success: success,
					error: "Could not " + name + " '" + this.selector+"'",
					bind: this,
					type: "action"
				});
				return this;
			}
		}

	for(var i=0; i < clicks.length; i++){
		makeClick(clicks[i])
	}

	$.extend(FuncUnit.prototype, {
		// perform check even if last queued item is a wait beacuse certain waits don't guarantee the element is visible (like text)
		_addExists: function(){
			this.exists(false);
		},
		/**
         * @function FuncUnit.prototype.type .type()
         * @parent actions
         * @signature `type(text [,success])`
         *
		 * Types text into an element.  This makes use of [Syn.type] and works in
		 * a very similar way.
		 *
Quick Examples

		 * @codestart
		 * //types hello world
		 *F('#bar').type('hello world')
		 *
		 * //submits a form by typing \r
		 *F("input[name=age]").type("27\r")
		 *
		 * //types FuncUnit, then deletes the Unit
		 *F('#foo').type("FuncUnit\b\b\b\b")
		 *
		 * //types JavaScriptMVC, then removes the MVC
		 *F('#zar').type("JavaScriptMVC[left][left][left]"+
		 *                      "[delete][delete][delete]")
		 *
		 * //types JavaScriptMVC, then selects the MVC and
		 * //deletes it
		 *F('#zar').type("JavaScriptMVC[shift]"+
		 *                "[left][left][left]"+
		 *                "[shift-up][delete]")
		 * @codeend
		 *
		 *
Characters

		 *
		 * For a list of the characters you can type, check [Syn.keycodes].
		 *
		 * @param {String} text the text you want to type
		 * @param {Function} [success] a callback that is run after typing, but before the next action.
		 * @return {FuncUnit} returns the funcUnit object for chaining.
		 */
		type: function( text, success ) {
			this._addExists();
			// when you type in something you have to click on it first
			this.click();
			var selector = this.selector;
			// type("") is a shortcut for clearing out a text input
			if(text === ""){
				text = "[ctrl]a[ctrl-up]\b"
			}
			FuncUnit.add({
				method : function(success, error){
					Syn("_type", text, this.bind[0], success);

				},
				success : success,
				error : "Could not type " + text + " into " + this.selector,
				bind : this,
				type: "action"
			});
			return this;
		},
    // TODO (DL) this needs to be deprecated this advertises .trigger() functionality
    // but expects that the target page will have jQuery which could not be the case.
		trigger: function(evName, success){
			this._addExists();
			FuncUnit.add({
				method : function(success, error){
					// need to use the page's jquery to trigger events
          if(!FuncUnit.win.jQuery) {
            throw 'Can not trigger custom event, no jQuery found on target page.';
          }
					FuncUnit.win.jQuery(this.bind.selector).trigger(evName)
					success()
				},
				success : success,
				error : "Could not trigger " + evName,
				bind : this,
				type: "action"
			});
			return this;
		},
		/**
         * @function FuncUnit.prototype.drag .drag()
         * @parent actions
         * @signature `drag(options [,success])`
		 * Drags an element into another element or coordinates.
		 * This takes the same paramameters as [Syn.prototype.move move].
		 * @param {String|Object} options A selector or coordinates describing the motion of the drag.
		 *
Options as a Selector

		 * Passing a string selector to drag the mouse.  The drag runs to the center of the element
		 * matched by the selector.  The following drags from the center of #foo to the center of #bar.
		 * @codestart
		 *F('#foo').drag('#bar')
		 * @codeend
		 *
Options as Coordinates

		 * You can pass in coordinates as clientX and clientY:
		 * @codestart
		 *F('#foo').drag('100x200')
		 * @codeend
		 * Or as pageX and pageY
		 * @codestart
		 *F('#foo').drag('100X200')
		 * @codeend
		 * Or relative to the start position
		 *F('#foo').drag('+10 +20')
		 *
Options as an Object

		 * You can configure the duration, start, and end point of a drag by passing in a json object.
		 * @codestart
		 * //drags from 0x0 to 100x100 in 2 seconds
		 *F('#foo').drag({
		 *   from: "0x0",
		 *   to: "100x100",
		 *   duration: 2000
		 * })
		 * @codeend
		 * @param {Function} [success] a callback that runs after the drag, but before the next action.
		 * @return {funcUnit} returns the funcunit object for chaining.
		 */
		drag: function( options, success ) {
			this._addExists();
			if(typeof options == 'string'){
				options = {to: options}
			}
			options.from = this.selector;

			var selector = this.selector;
			FuncUnit.add({
				method: function(success, error){
					Syn("_drag", options, this.bind[0],success);
				},
				success: success,
				error: "Could not drag " + this.selector,
				bind: this,
				type: "action"
			})
			return this;
		},
		/**
         * @function FuncUnit.prototype.move .move()
         * @parent actions
         * @signature `move(options [,success])`
		 * Moves an element into another element or coordinates.  This will trigger mouseover
		 * mouseouts accordingly.
		 * This takes the same paramameters as [Syn.prototype.move move].
		 * @param {String|Object} options A selector or coordinates describing the motion of the move.
		 *
Options as a Selector

		 * Passing a string selector to move the mouse.  The move runs to the center of the element
		 * matched by the selector.  The following moves from the center of #foo to the center of #bar.
		 * @codestart
		 *F('#foo').move('#bar')
		 * @codeend
		 *
Options as Coordinates

		 * You can pass in coordinates as clientX and clientY:
		 * @codestart
		 *F('#foo').move('100x200')
		 * @codeend
		 * Or as pageX and pageY
		 * @codestart
		 *F('#foo').move('100X200')
		 * @codeend
		 * Or relative to the start position
		 *F('#foo').move('+10 +20')
		 *
Options as an Object

		 * You can configure the duration, start, and end point of a move by passing in a json object.
		 * @codestart
		 * //drags from 0x0 to 100x100 in 2 seconds
		 *F('#foo').move({
		 *   from: "0x0",
		 *   to: "100x100",
		 *   duration: 2000
		 * })
		 * @codeend
		 * @param {Function} [success] a callback that runs after the drag, but before the next action.
		 * @return {funcUnit} returns the funcunit object for chaining.
		 */
		move: function( options, success ) {
			this._addExists();
			if(typeof options == 'string'){
				options = {to: options}
			}
			options.from = this.selector;

			var selector = this.selector;
			FuncUnit.add({
				method: function(success, error){
					Syn("_move", options, this.bind[0], success);
				},
				success: success,
				error: "Could not move " + this.selector,
				bind: this,
				type: "action"
			});
			return this;
		},
		/**
         * @function FuncUnit.prototype.scroll .scroll()
         * @parent actions
         * @signature `scroll(direction, amount, success)`
		 * Scrolls an element in a particular direction by setting the scrollTop or scrollLeft.
		 * @param {String} direction "left" or "top"
		 * @param {Number} amount number of pixels to scroll
		 * @param {Function} success
		 */
		scroll: function( direction, amount, success ) {
			this._addExists();
			var selector = this.selector,
				direction;
			if (direction == "left" || direction == "right") {
				direction = "Left";
			} else if (direction == "top" || direction == "bottom") {
				direction = "Top";
			}
			FuncUnit.add({
				method: function(success, error){
					this.bind.each(function(i, el){
						this["scroll" + direction] = amount;
					})
					success();
				},
				success: success,
				error: "Could not scroll " + this.selector,
				bind: this,
				type: "action"
			});
			return this;
		}
	})
	return FuncUnit;
});
<|endoftext|>


<commit_before>import Integration from './../Integration.js';
import deleteProperty from './../functions/deleteProperty';
import { getProp } from './../functions/dotProp';
import { COMPLETED_TRANSACTION, LEAD } from '/events';
import cookie from 'js-cookie';

function getScreenResolution() {
  return `${window.screen.width}x${window.screen.height}`;
}

const PAYMENT_TYPE_SALE = 'sale';
const PAYMENT_TYPE_LEAD = 'lead';

class Admitad extends Integration {

  constructor(digitalData, options) {
    const optionsWithDefaults = Object.assign({
      campaignCode: '',
      paymentType: 'sale',
      defaultActionCode: '1',
      responseType: 'img',
      cookieName: 'admitad_uid',
    }, options);

    super(digitalData, optionsWithDefaults);

    this._isLoaded = false;

    this.addTag('trackingPixel', {
      type: 'script',
      attr: {
        id: '_admitad-pixel',
        src: `//cdn.asbmit.com/static/js/ad/pixel.min.js?r=${Date.now()}`,
      },
    });
  }

  initialize() {
    this._isLoaded = true;
    this.onLoad();
  }

  getEnrichableEventProps(event) {
    let enrichableProps = [];

    if (event.name === COMPLETED_TRANSACTION) {
      enrichableProps = [
        'transaction',
        'user.userId',
        'website.currency',
      ];
    } else if (event.name === LEAD) {
      enrichableProps = [
        'user.userId',
      ];
    }

    return enrichableProps;
  }

  isLoaded() {
    return this._isLoaded;
  }

  reset() {
    deleteProperty(window, '_admitadPixel');
    deleteProperty(window, '_admitadPositions');
  }

  trackEvent(event) {
    const uid = cookie.get(this.getOption('cookieName'));
    if (!uid) return;

    if (event.name === COMPLETED_TRANSACTION && this.getOption('paymentType') === PAYMENT_TYPE_SALE) {
      this.trackSale(event, uid);
    } else if (event.name === LEAD && this.getOption('paymentType') === PAYMENT_TYPE_LEAD) {
      this.trackLead(event, uid);
    }
  }

  setupPixel(event) {
    window._admitadPixel = {
      response_type: this.getOption('responseType'),
      action_code: getProp('admitad.actionCode', event) || this.getOption('defaultActionCode'),
      campaign_code: this.getOption('campaignCode'),
    };
    window._admitadPositions = window._admitadPositions || [];
  }

  trackSale(event, uid) {
    const transaction = event.transaction;
    if (!transaction || !transaction.lineItems || !transaction.lineItems.length) {
      return;
    }

    this.setupPixel(event);

    const lineItems = transaction.lineItems;
    let index = 1;
    for (const lineItem of lineItems) {
      window._admitadPositions.push({
        uid: uid,
        order_id: transaction.orderId,
        position_id: index,
        client_id: getProp('user.userId', event),
        tariff_code: getProp('admitad.tariffCode', lineItem) || '1',
        currency_code: getProp('product.currency', lineItem) || getProp('website.currency', event) || '',
        position_count: lineItems.length,
        price: getProp('product.unitSalePrice', lineItem) || getProp('product.unitPrice', lineItem),
        quantity: lineItem.quantity || 1,
        product_id: getProp('product.id', lineItem),
        screen: getScreenResolution(),
        old_customer: (transaction.isFirst === false) ? 1 : 0,
        coupon: (transaction.vouchers && transaction.vouchers.length) ? 1 : 0,
        payment_type: PAYMENT_TYPE_SALE,
      });
      index += 1;
    }

    this.load('trackingPixel');
  }

  trackLead(event, uid) {
    this.setupPixel(event);
    window._admitadPositions.push({
      uid: uid,
      order_id: event.leadId,
      client_id: getProp('user.userId', event),
      tariff_code: getProp('admitad.tariffCode', event) || '1',
      screen: getScreenResolution(),
      payment_type: PAYMENT_TYPE_LEAD,
    });
  }
}

export default Admitad;
<commit_msg>added cookie tracking<commit_after>import Integration from './../Integration.js';
import deleteProperty from './../functions/deleteProperty';
import getQueryParam from './../functions/getQueryParam';
import topDomain from './../functions/topDomain';
import { getProp } from './../functions/dotProp';
import { COMPLETED_TRANSACTION, LEAD } from '/events';
import cookie from 'js-cookie';

function getScreenResolution() {
  return `${window.screen.width}x${window.screen.height}`;
}

const PAYMENT_TYPE_SALE = 'sale';
const PAYMENT_TYPE_LEAD = 'lead';
const UID_GET_PARAM = 'admitad_uid';
const DEFAULT_COOKIE_NAME = 'admitad_uid';

class Admitad extends Integration {

  constructor(digitalData, options) {
    const optionsWithDefaults = Object.assign({
      campaignCode: '',
      paymentType: PAYMENT_TYPE_SALE,
      defaultActionCode: '1',
      responseType: 'img',
      cookieName: DEFAULT_COOKIE_NAME,
      cookieTracking: true, // false - if advertiser wants to track cookies by itself
      cookieDomain: topDomain(window.location.href),
      cookieTtl: 90, // days
    }, options);

    super(digitalData, optionsWithDefaults);

    this._isLoaded = false;

    this.addTag('trackingPixel', {
      type: 'script',
      attr: {
        id: '_admitad-pixel',
        src: `//cdn.asbmit.com/static/js/ad/pixel.min.js?r=${Date.now()}`,
      },
    });
  }

  initialize() {
    this._isLoaded = true;

    if (this.getOption('cookieTracking')) {
      this.addAffiliateCookie();
    }

    this.onLoad();
  }

  addAffiliateCookie() {
    if (window.self !== window.top) {
      return; // protect from iframe cookie-stuffing
    }

    const uidInQuery = getQueryParam(UID_GET_PARAM);
    if (uidInQuery) {
      cookie.set(this.getOption('cookieName'), uidInQuery, {
        expires: this.getOption('cookieTtl'),
        domain: this.getOption('cookieDomain'),
      });
    }
  }

  getEnrichableEventProps(event) {
    let enrichableProps = [];

    if (event.name === COMPLETED_TRANSACTION) {
      enrichableProps = [
        'transaction',
        'user.userId',
        'website.currency',
      ];
    } else if (event.name === LEAD) {
      enrichableProps = [
        'user.userId',
      ];
    }

    return enrichableProps;
  }

  isLoaded() {
    return this._isLoaded;
  }

  reset() {
    deleteProperty(window, '_admitadPixel');
    deleteProperty(window, '_admitadPositions');
  }

  trackEvent(event) {
    const uid = cookie.get(this.getOption('cookieName'));
    if (!uid) return;

    if (event.name === COMPLETED_TRANSACTION && this.getOption('paymentType') === PAYMENT_TYPE_SALE) {
      this.trackSale(event, uid);
    } else if (event.name === LEAD && this.getOption('paymentType') === PAYMENT_TYPE_LEAD) {
      this.trackLead(event, uid);
    }
  }

  setupPixel(event) {
    window._admitadPixel = {
      response_type: this.getOption('responseType'),
      action_code: getProp('admitad.actionCode', event) || this.getOption('defaultActionCode'),
      campaign_code: this.getOption('campaignCode'),
    };
    window._admitadPositions = window._admitadPositions || [];
  }

  trackSale(event, uid) {
    const transaction = event.transaction;
    if (!transaction || !transaction.lineItems || !transaction.lineItems.length) {
      return;
    }

    this.setupPixel(event);

    const lineItems = transaction.lineItems;
    let index = 1;
    for (const lineItem of lineItems) {
      window._admitadPositions.push({
        uid: uid,
        order_id: transaction.orderId,
        position_id: index,
        client_id: getProp('user.userId', event),
        tariff_code: getProp('admitad.tariffCode', lineItem) || '1',
        currency_code: getProp('product.currency', lineItem) || getProp('website.currency', event) || '',
        position_count: lineItems.length,
        price: getProp('product.unitSalePrice', lineItem) || getProp('product.unitPrice', lineItem),
        quantity: lineItem.quantity || 1,
        product_id: getProp('product.id', lineItem),
        screen: getScreenResolution(),
        old_customer: (transaction.isFirst === false) ? 1 : 0,
        coupon: (transaction.vouchers && transaction.vouchers.length) ? 1 : 0,
        payment_type: PAYMENT_TYPE_SALE,
      });
      index += 1;
    }

    this.load('trackingPixel');
  }

  trackLead(event, uid) {
    if (!event.lead || !event.lead.id) {
      return;
    }

    this.setupPixel(event);

    window._admitadPositions.push({
      uid: uid,
      order_id: getProp('lead.id', event),
      client_id: getProp('user.userId', event),
      tariff_code: getProp('admitad.tariffCode', event) || '1',
      screen: getScreenResolution(),
      payment_type: PAYMENT_TYPE_LEAD,
    });

    this.load('trackingPixel');
  }
}

export default Admitad;
<|endoftext|>


<commit_before>define("rsvp/all",
  ["rsvp/defer","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var defer = __dependency1__.defer;

    function all(promises) {
      var results = [], deferred = defer(), remaining = promises.length;

      if (remaining === 0) {
        deferred.resolve([]);
      }

      var resolver = function(index) {
        return function(value) {
          resolveAll(index, value);
        };
      };

      var resolveAll = function(index, value) {
        results[index] = value;
        if (--remaining === 0) {
          deferred.resolve(results);
        }
      };

      var rejectAll = function(error) {
        deferred.reject(error);
      };

      for (var i = 0; i < promises.length; i++) {
        if (promises[i] && typeof promises[i].then === 'function') {
          promises[i].then(resolver(i), rejectAll);
        } else {
          resolveAll(i, promises[i]);
        }
      }
      return deferred.promise;
    }

    __exports__.all = all;
  });

define("rsvp/async",
  ["exports"],
  function(__exports__) {
    "use strict";
    var browserGlobal = (typeof window !== 'undefined') ? window : {};

    var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
    var async;

    if (typeof process !== 'undefined' &&
      {}.toString.call(process) === '[object process]') {
      async = function(callback, binding) {
        process.nextTick(function() {
          callback.call(binding);
        });
      };
    } else if (BrowserMutationObserver) {
      var queue = [];

      var observer = new BrowserMutationObserver(function() {
        var toProcess = queue.slice();
        queue = [];

        toProcess.forEach(function(tuple) {
          var callback = tuple[0], binding = tuple[1];
          callback.call(binding);
        });
      });

      var element = document.createElement('div');
      observer.observe(element, { attributes: true });

      // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
      window.addEventListener('unload', function(){
        observer.disconnect();
        observer = null;
      });

      async = function(callback, binding) {
        queue.push([callback, binding]);
        element.setAttribute('drainQueue', 'drainQueue');
      };
    } else {
      async = function(callback, binding) {
        setTimeout(function() {
          callback.call(binding);
        }, 1);
      };
    }

    __exports__.async = async;
  });

define("rsvp/config",
  ["rsvp/async","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var async = __dependency1__.async;

    var config = {};
    config.async = async;

    __exports__.config = config;
  });

define("rsvp/defer",
  ["rsvp/promise","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;

    function defer() {
      var deferred = {};

      var promise = new Promise(function(resolve, reject) {
        deferred.resolve = resolve;
        deferred.reject = reject;
      });

      deferred.promise = promise;
      return deferred;
    }

    __exports__.defer = defer;
  });

define("rsvp/events",
  ["exports"],
  function(__exports__) {
    "use strict";
    var Event = function(type, options) {
      this.type = type;

      for (var option in options) {
        if (!options.hasOwnProperty(option)) { continue; }

        this[option] = options[option];
      }
    };

    var indexOf = function(callbacks, callback) {
      for (var i=0, l=callbacks.length; i<l; i++) {
        if (callbacks[i][0] === callback) { return i; }
      }

      return -1;
    };

    var callbacksFor = function(object) {
      var callbacks = object._promiseCallbacks;

      if (!callbacks) {
        callbacks = object._promiseCallbacks = {};
      }

      return callbacks;
    };

    var EventTarget = {
      mixin: function(object) {
        object.on = this.on;
        object.off = this.off;
        object.trigger = this.trigger;
        return object;
      },

      on: function(eventNames, callback, binding) {
        var allCallbacks = callbacksFor(this), callbacks, eventName;
        eventNames = eventNames.split(/\s+/);
        binding = binding || this;

        while (eventName = eventNames.shift()) {
          callbacks = allCallbacks[eventName];

          if (!callbacks) {
            callbacks = allCallbacks[eventName] = [];
          }

          if (indexOf(callbacks, callback) === -1) {
            callbacks.push([callback, binding]);
          }
        }
      },

      off: function(eventNames, callback) {
        var allCallbacks = callbacksFor(this), callbacks, eventName, index;
        eventNames = eventNames.split(/\s+/);

        while (eventName = eventNames.shift()) {
          if (!callback) {
            allCallbacks[eventName] = [];
            continue;
          }

          callbacks = allCallbacks[eventName];

          index = indexOf(callbacks, callback);

          if (index !== -1) { callbacks.splice(index, 1); }
        }
      },

      trigger: function(eventName, options) {
        var allCallbacks = callbacksFor(this),
            callbacks, callbackTuple, callback, binding, event;

        if (callbacks = allCallbacks[eventName]) {
          // Don't cache the callbacks.length since it may grow
          for (var i=0; i<callbacks.length; i++) {
            callbackTuple = callbacks[i];
            callback = callbackTuple[0];
            binding = callbackTuple[1];

            if (typeof options !== 'object') {
              options = { detail: options };
            }

            event = new Event(eventName, options);
            callback.call(binding, event);
          }
        }
      }
    };

    __exports__.EventTarget = EventTarget;
  });

define("rsvp/hash",
  ["rsvp/defer","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var defer = __dependency1__.defer;

    function size(object) {
      var size = 0;

      for (var prop in object) {
        size++;
      }

      return size;
    }

    function hash(promises) {
      var results = {}, deferred = defer(), remaining = size(promises);

      if (remaining === 0) {
        deferred.resolve({});
      }

      var resolver = function(prop) {
        return function(value) {
          resolveAll(prop, value);
        };
      };

      var resolveAll = function(prop, value) {
        results[prop] = value;
        if (--remaining === 0) {
          deferred.resolve(results);
        }
      };

      var rejectAll = function(error) {
        deferred.reject(error);
      };

      for (var prop in promises) {
        if (promises[prop] && typeof promises[prop].then === 'function') {
          promises[prop].then(resolver(prop), rejectAll);
        } else {
          resolveAll(prop, promises[prop]);
        }
      }

      return deferred.promise;
    }

    __exports__.hash = hash;
  });

define("rsvp/node",
  ["rsvp/promise","rsvp/all","exports"],
  function(__dependency1__, __dependency2__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;
    var all = __dependency2__.all;

    function makeNodeCallbackFor(resolve, reject) {
      return function (error, value) {
        if (error) {
          reject(error);
        } else if (arguments.length > 2) {
          resolve(Array.prototype.slice.call(arguments, 1));
        } else {
          resolve(value);
        }
      };
    }

    function denodeify(nodeFunc) {
      return function()  {
        var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;

        var promise = new Promise(function(nodeResolve, nodeReject) {
          resolve = nodeResolve;
          reject = nodeReject;
        });

        all(nodeArgs).then(function(nodeArgs) {
          nodeArgs.push(makeNodeCallbackFor(resolve, reject));

          try {
            nodeFunc.apply(this, nodeArgs);
          } catch(e) {
            reject(e);
          }
        });

        return promise;
      };
    }

    __exports__.denodeify = denodeify;
  });

define("rsvp/promise",
  ["rsvp/config","rsvp/events","exports"],
  function(__dependency1__, __dependency2__, __exports__) {
    "use strict";
    var config = __dependency1__.config;
    var EventTarget = __dependency2__.EventTarget;

    function objectOrFunction(x) {
      return isFunction(x) || (typeof x === "object" && x !== null);
    }

    function isFunction(x){
      return typeof x === "function";
    }

    var Promise = function(resolver) {
      var promise = this,
      resolved = false;

      if (typeof resolver !== 'function') {
        throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
      }

      if (!(promise instanceof Promise)) {
        return new Promise(resolver);
      }

      var resolvePromise = function(value) {
        if (resolved) { return; }
        resolved = true;
        resolve(promise, value);
      };

      var rejectPromise = function(value) {
        if (resolved) { return; }
        resolved = true;
        reject(promise, value);
      };

      this.on('promise:resolved', function(event) {
        this.trigger('success', { detail: event.detail });
      }, this);

      this.on('promise:failed', function(event) {
        this.trigger('error', { detail: event.detail });
      }, this);

      resolver(resolvePromise, rejectPromise);
    };

    var invokeCallback = function(type, promise, callback, event) {
      var hasCallback = isFunction(callback),
          value, error, succeeded, failed;

      if (hasCallback) {
        try {
          value = callback(event.detail);
          succeeded = true;
        } catch(e) {
          failed = true;
          error = e;
        }
      } else {
        value = event.detail;
        succeeded = true;
      }

      if (handleThenable(promise, value)) {
        return;
      } else if (hasCallback && succeeded) {
        resolve(promise, value);
      } else if (failed) {
        reject(promise, error);
      } else if (type === 'resolve') {
        resolve(promise, value);
      } else if (type === 'reject') {
        reject(promise, value);
      }
    };

    Promise.prototype = {
      constructor: Promise,

      then: function(done, fail) {
        var thenPromise = new Promise(function() {});

        if (this.isFulfilled) {
          config.async(function() {
            invokeCallback('resolve', thenPromise, done, { detail: this.fulfillmentValue });
          }, this);
        }

        if (this.isRejected) {
          config.async(function() {
            invokeCallback('reject', thenPromise, fail, { detail: this.rejectedReason });
          }, this);
        }

        this.on('promise:resolved', function(event) {
          invokeCallback('resolve', thenPromise, done, event);
        });

        this.on('promise:failed', function(event) {
          invokeCallback('reject', thenPromise, fail, event);
        });

        return thenPromise;
      }
    };

    EventTarget.mixin(Promise.prototype);

    function resolve(promise, value) {
      if (promise === value) {
        fulfill(promise, value);
      } else if (!handleThenable(promise, value)) {
        fulfill(promise, value);
      }
    }

    function handleThenable(promise, value) {
      var then = null;

      if (objectOrFunction(value)) {
        try {
          then = value.then;
        } catch(e) {
          reject(promise, e);
          return true;
        }

        if (isFunction(then)) {
          try {
            then.call(value, function(val) {
              if (value !== val) {
                resolve(promise, val);
              } else {
                fulfill(promise, val);
              }
            }, function(val) {
              reject(promise, val);
            });
          } catch (e) {
            reject(promise, e);
          }
          return true;
        }
      }

      return false;
    }

    function fulfill(promise, value) {
      config.async(function() {
        promise.trigger('promise:resolved', { detail: value });
        promise.isFulfilled = true;
        promise.fulfillmentValue = value;
      });
    }

    function reject(promise, value) {
      config.async(function() {
        promise.trigger('promise:failed', { detail: value });
        promise.isRejected = true;
        promise.rejectedReason = value;
      });
    }

    __exports__.Promise = Promise;
  });

define("rsvp/resolve",
  ["rsvp/promise","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;

    function objectOrFunction(x) {
      return typeof x === "function" || (typeof x === "object" && x !== null);
    }

    function resolve(thenable){
      var promise = new Promise(function(resolve, reject){
        var then;

        try {
          if ( objectOrFunction(thenable) ) {
            then = thenable.then;

            if (typeof then === "function") {
              then.call(thenable, resolve, reject);
            } else {
              resolve(thenable);
            }

          } else {
            resolve(thenable);
          }

        } catch(error) {
          reject(error);
        }
      });

      return promise;
    }

    __exports__.resolve = resolve;
  });

define("rsvp",
  ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/defer","rsvp/config","rsvp/resolve","exports"],
  function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) {
    "use strict";
    var EventTarget = __dependency1__.EventTarget;
    var Promise = __dependency2__.Promise;
    var denodeify = __dependency3__.denodeify;
    var all = __dependency4__.all;
    var hash = __dependency5__.hash;
    var defer = __dependency6__.defer;
    var config = __dependency7__.config;
    var resolve = __dependency8__.resolve;

    function configure(name, value) {
      config[name] = value;
    }

    __exports__.Promise = Promise;
    __exports__.EventTarget = EventTarget;
    __exports__.all = all;
    __exports__.hash = hash;
    __exports__.defer = defer;
    __exports__.denodeify = denodeify;
    __exports__.configure = configure;
    __exports__.resolve = resolve;
  });
<commit_msg>Update rsvp to include rejection on resolver exception<commit_after>define("rsvp/all",
  ["rsvp/defer","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var defer = __dependency1__.defer;

    function all(promises) {
      var results = [], deferred = defer(), remaining = promises.length;

      if (remaining === 0) {
        deferred.resolve([]);
      }

      var resolver = function(index) {
        return function(value) {
          resolveAll(index, value);
        };
      };

      var resolveAll = function(index, value) {
        results[index] = value;
        if (--remaining === 0) {
          deferred.resolve(results);
        }
      };

      var rejectAll = function(error) {
        deferred.reject(error);
      };

      for (var i = 0; i < promises.length; i++) {
        if (promises[i] && typeof promises[i].then === 'function') {
          promises[i].then(resolver(i), rejectAll);
        } else {
          resolveAll(i, promises[i]);
        }
      }
      return deferred.promise;
    }

    __exports__.all = all;
  });

define("rsvp/async",
  ["exports"],
  function(__exports__) {
    "use strict";
    var browserGlobal = (typeof window !== 'undefined') ? window : {};

    var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
    var async;

    if (typeof process !== 'undefined' &&
      {}.toString.call(process) === '[object process]') {
      async = function(callback, binding) {
        process.nextTick(function() {
          callback.call(binding);
        });
      };
    } else if (BrowserMutationObserver) {
      var queue = [];

      var observer = new BrowserMutationObserver(function() {
        var toProcess = queue.slice();
        queue = [];

        toProcess.forEach(function(tuple) {
          var callback = tuple[0], binding = tuple[1];
          callback.call(binding);
        });
      });

      var element = document.createElement('div');
      observer.observe(element, { attributes: true });

      // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
      window.addEventListener('unload', function(){
        observer.disconnect();
        observer = null;
      });

      async = function(callback, binding) {
        queue.push([callback, binding]);
        element.setAttribute('drainQueue', 'drainQueue');
      };
    } else {
      async = function(callback, binding) {
        setTimeout(function() {
          callback.call(binding);
        }, 1);
      };
    }

    __exports__.async = async;
  });

define("rsvp/config",
  ["rsvp/async","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var async = __dependency1__.async;

    var config = {};
    config.async = async;

    __exports__.config = config;
  });

define("rsvp/defer",
  ["rsvp/promise","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;

    function defer() {
      var deferred = {};

      var promise = new Promise(function(resolve, reject) {
        deferred.resolve = resolve;
        deferred.reject = reject;
      });

      deferred.promise = promise;
      return deferred;
    }

    __exports__.defer = defer;
  });

define("rsvp/events",
  ["exports"],
  function(__exports__) {
    "use strict";
    var Event = function(type, options) {
      this.type = type;

      for (var option in options) {
        if (!options.hasOwnProperty(option)) { continue; }

        this[option] = options[option];
      }
    };

    var indexOf = function(callbacks, callback) {
      for (var i=0, l=callbacks.length; i<l; i++) {
        if (callbacks[i][0] === callback) { return i; }
      }

      return -1;
    };

    var callbacksFor = function(object) {
      var callbacks = object._promiseCallbacks;

      if (!callbacks) {
        callbacks = object._promiseCallbacks = {};
      }

      return callbacks;
    };

    var EventTarget = {
      mixin: function(object) {
        object.on = this.on;
        object.off = this.off;
        object.trigger = this.trigger;
        return object;
      },

      on: function(eventNames, callback, binding) {
        var allCallbacks = callbacksFor(this), callbacks, eventName;
        eventNames = eventNames.split(/\s+/);
        binding = binding || this;

        while (eventName = eventNames.shift()) {
          callbacks = allCallbacks[eventName];

          if (!callbacks) {
            callbacks = allCallbacks[eventName] = [];
          }

          if (indexOf(callbacks, callback) === -1) {
            callbacks.push([callback, binding]);
          }
        }
      },

      off: function(eventNames, callback) {
        var allCallbacks = callbacksFor(this), callbacks, eventName, index;
        eventNames = eventNames.split(/\s+/);

        while (eventName = eventNames.shift()) {
          if (!callback) {
            allCallbacks[eventName] = [];
            continue;
          }

          callbacks = allCallbacks[eventName];

          index = indexOf(callbacks, callback);

          if (index !== -1) { callbacks.splice(index, 1); }
        }
      },

      trigger: function(eventName, options) {
        var allCallbacks = callbacksFor(this),
            callbacks, callbackTuple, callback, binding, event;

        if (callbacks = allCallbacks[eventName]) {
          // Don't cache the callbacks.length since it may grow
          for (var i=0; i<callbacks.length; i++) {
            callbackTuple = callbacks[i];
            callback = callbackTuple[0];
            binding = callbackTuple[1];

            if (typeof options !== 'object') {
              options = { detail: options };
            }

            event = new Event(eventName, options);
            callback.call(binding, event);
          }
        }
      }
    };

    __exports__.EventTarget = EventTarget;
  });

define("rsvp/hash",
  ["rsvp/defer","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var defer = __dependency1__.defer;

    function size(object) {
      var size = 0;

      for (var prop in object) {
        size++;
      }

      return size;
    }

    function hash(promises) {
      var results = {}, deferred = defer(), remaining = size(promises);

      if (remaining === 0) {
        deferred.resolve({});
      }

      var resolver = function(prop) {
        return function(value) {
          resolveAll(prop, value);
        };
      };

      var resolveAll = function(prop, value) {
        results[prop] = value;
        if (--remaining === 0) {
          deferred.resolve(results);
        }
      };

      var rejectAll = function(error) {
        deferred.reject(error);
      };

      for (var prop in promises) {
        if (promises[prop] && typeof promises[prop].then === 'function') {
          promises[prop].then(resolver(prop), rejectAll);
        } else {
          resolveAll(prop, promises[prop]);
        }
      }

      return deferred.promise;
    }

    __exports__.hash = hash;
  });

define("rsvp/node",
  ["rsvp/promise","rsvp/all","exports"],
  function(__dependency1__, __dependency2__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;
    var all = __dependency2__.all;

    function makeNodeCallbackFor(resolve, reject) {
      return function (error, value) {
        if (error) {
          reject(error);
        } else if (arguments.length > 2) {
          resolve(Array.prototype.slice.call(arguments, 1));
        } else {
          resolve(value);
        }
      };
    }

    function denodeify(nodeFunc) {
      return function()  {
        var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;

        var promise = new Promise(function(nodeResolve, nodeReject) {
          resolve = nodeResolve;
          reject = nodeReject;
        });

        all(nodeArgs).then(function(nodeArgs) {
          nodeArgs.push(makeNodeCallbackFor(resolve, reject));

          try {
            nodeFunc.apply(this, nodeArgs);
          } catch(e) {
            reject(e);
          }
        });

        return promise;
      };
    }

    __exports__.denodeify = denodeify;
  });

define("rsvp/promise",
  ["rsvp/config","rsvp/events","exports"],
  function(__dependency1__, __dependency2__, __exports__) {
    "use strict";
    var config = __dependency1__.config;
    var EventTarget = __dependency2__.EventTarget;

    function objectOrFunction(x) {
      return isFunction(x) || (typeof x === "object" && x !== null);
    }

    function isFunction(x){
      return typeof x === "function";
    }

    var Promise = function(resolver) {
      var promise = this,
      resolved = false;

      if (typeof resolver !== 'function') {
        throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
      }

      if (!(promise instanceof Promise)) {
        return new Promise(resolver);
      }

      var resolvePromise = function(value) {
        if (resolved) { return; }
        resolved = true;
        resolve(promise, value);
      };

      var rejectPromise = function(value) {
        if (resolved) { return; }
        resolved = true;
        reject(promise, value);
      };

      this.on('promise:resolved', function(event) {
        this.trigger('success', { detail: event.detail });
      }, this);

      this.on('promise:failed', function(event) {
        this.trigger('error', { detail: event.detail });
      }, this);

      try {
        resolver(resolvePromise, rejectPromise);
      } catch(e) {
        rejectPromise(e);
      }
    };

    var invokeCallback = function(type, promise, callback, event) {
      var hasCallback = isFunction(callback),
          value, error, succeeded, failed;

      if (hasCallback) {
        try {
          value = callback(event.detail);
          succeeded = true;
        } catch(e) {
          failed = true;
          error = e;
        }
      } else {
        value = event.detail;
        succeeded = true;
      }

      if (handleThenable(promise, value)) {
        return;
      } else if (hasCallback && succeeded) {
        resolve(promise, value);
      } else if (failed) {
        reject(promise, error);
      } else if (type === 'resolve') {
        resolve(promise, value);
      } else if (type === 'reject') {
        reject(promise, value);
      }
    };

    Promise.prototype = {
      constructor: Promise,

      then: function(done, fail) {
        var thenPromise = new Promise(function() {});

        if (this.isFulfilled) {
          config.async(function() {
            invokeCallback('resolve', thenPromise, done, { detail: this.fulfillmentValue });
          }, this);
        }

        if (this.isRejected) {
          config.async(function() {
            invokeCallback('reject', thenPromise, fail, { detail: this.rejectedReason });
          }, this);
        }

        this.on('promise:resolved', function(event) {
          invokeCallback('resolve', thenPromise, done, event);
        });

        this.on('promise:failed', function(event) {
          invokeCallback('reject', thenPromise, fail, event);
        });

        return thenPromise;
      }
    };

    EventTarget.mixin(Promise.prototype);

    function resolve(promise, value) {
      if (promise === value) {
        fulfill(promise, value);
      } else if (!handleThenable(promise, value)) {
        fulfill(promise, value);
      }
    }

    function handleThenable(promise, value) {
      var then = null;

      if (objectOrFunction(value)) {
        try {
          then = value.then;
        } catch(e) {
          reject(promise, e);
          return true;
        }

        if (isFunction(then)) {
          try {
            then.call(value, function(val) {
              if (value !== val) {
                resolve(promise, val);
              } else {
                fulfill(promise, val);
              }
            }, function(val) {
              reject(promise, val);
            });
          } catch (e) {
            reject(promise, e);
          }
          return true;
        }
      }

      return false;
    }

    function fulfill(promise, value) {
      config.async(function() {
        promise.trigger('promise:resolved', { detail: value });
        promise.isFulfilled = true;
        promise.fulfillmentValue = value;
      });
    }

    function reject(promise, value) {
      config.async(function() {
        promise.trigger('promise:failed', { detail: value });
        promise.isRejected = true;
        promise.rejectedReason = value;
      });
    }

    __exports__.Promise = Promise;
  });

define("rsvp/resolve",
  ["rsvp/promise","exports"],
  function(__dependency1__, __exports__) {
    "use strict";
    var Promise = __dependency1__.Promise;

    function objectOrFunction(x) {
      return typeof x === "function" || (typeof x === "object" && x !== null);
    }

    function resolve(thenable){
      var promise = new Promise(function(resolve, reject){
        var then;

        try {
          if ( objectOrFunction(thenable) ) {
            then = thenable.then;

            if (typeof then === "function") {
              then.call(thenable, resolve, reject);
            } else {
              resolve(thenable);
            }

          } else {
            resolve(thenable);
          }

        } catch(error) {
          reject(error);
        }
      });

      return promise;
    }

    __exports__.resolve = resolve;
  });

define("rsvp",
  ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/defer","rsvp/config","rsvp/resolve","exports"],
  function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) {
    "use strict";
    var EventTarget = __dependency1__.EventTarget;
    var Promise = __dependency2__.Promise;
    var denodeify = __dependency3__.denodeify;
    var all = __dependency4__.all;
    var hash = __dependency5__.hash;
    var defer = __dependency6__.defer;
    var config = __dependency7__.config;
    var resolve = __dependency8__.resolve;

    function configure(name, value) {
      config[name] = value;
    }

    __exports__.Promise = Promise;
    __exports__.EventTarget = EventTarget;
    __exports__.all = all;
    __exports__.hash = hash;
    __exports__.defer = defer;
    __exports__.denodeify = denodeify;
    __exports__.configure = configure;
    __exports__.resolve = resolve;
  });
<|endoftext|>


<commit_before>angular.module('tutorials', [])

.directive('docTutorialNav', function(templateMerge) {
  var pages = [
    '',
    'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
    'step_05', 'step_06', 'step_07', 'step_08', 'step_09',
    'step_10', 'step_11', 'step_12', 'the_end'
  ];
  return {
    compile: function(element, attrs) {
      var seq = 1 * attrs.docTutorialNav,
          props = {
            seq: seq,
            prev: pages[seq],
            next: pages[2 + seq],
            diffLo: seq ? (seq - 1): '0~1',
            diffHi: seq
          };

      element.addClass('btn-group');
      element.addClass('tutorial-nav');
      element.append(templateMerge(
        '
 Previous
\n' +
        '
 Live Demo
\n' +
        '
 Code Diff
\n' +
        '
Next
', props));
    }
  };
})

.directive('docTutorialReset', function() {
  function tab(name, command, id, step) {
    return '' +
      '
\n' +
      '
\n' +
      '

Reset the workspace to step ' + step + '.
' +
      '
' + command + '

\n' +
      '

Refresh your browser or check the app out on Angular\'s server.

\n' +
      '
\n' +
      '
\n';
  }

  return {
    compile: function(element, attrs) {
      var step = attrs.docTutorialReset;
      element.html(
        '
' +
          '
Workspace Reset Instructions  ➤
' +
        '
\n' +
        '
\n' +
          tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
          tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
        '
\n');
    }
  };
});
<commit_msg>docs(tutorial): display button icons (Previous, Live Demo, ...)<commit_after>angular.module('tutorials', [])

.directive('docTutorialNav', function(templateMerge) {
  var pages = [
    '',
    'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
    'step_05', 'step_06', 'step_07', 'step_08', 'step_09',
    'step_10', 'step_11', 'step_12', 'the_end'
  ];
  return {
    compile: function(element, attrs) {
      var seq = 1 * attrs.docTutorialNav,
          props = {
            seq: seq,
            prev: pages[seq],
            next: pages[2 + seq],
            diffLo: seq ? (seq - 1): '0~1',
            diffHi: seq
          };

      element.addClass('btn-group');
      element.addClass('tutorial-nav');
      element.append(templateMerge(
        '
 Previous
\n' +
        '
 Live Demo
\n' +
        '
 Code Diff
\n' +
        '
Next
', props));
    }
  };
})

.directive('docTutorialReset', function() {
  function tab(name, command, id, step) {
    return '' +
      '
\n' +
      '
\n' +
      '

Reset the workspace to step ' + step + '.
' +
      '
' + command + '

\n' +
      '

Refresh your browser or check the app out on Angular\'s server.

\n' +
      '
\n' +
      '
\n';
  }

  return {
    compile: function(element, attrs) {
      var step = attrs.docTutorialReset;
      element.html(
        '
' +
          '
Workspace Reset Instructions  ➤
' +
        '
\n' +
        '
\n' +
          tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
          tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
        '
\n');
    }
  };
});
<|endoftext|>