<commit_before>import Vuex from 'vuex';
import { storiesOf } from '@storybook/vue';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { withNotes } from '@storybook/addon-notes';
import {
  withKnobs,
  text,
  number,
  boolean,
  array,
  select,
  color,
  date,
} from '@storybook/addon-knobs';
import Centered from '@storybook/addon-centered';

import MyButton from './Button.vue';
import Welcome from './Welcome.vue';
import App from '../App.vue';

storiesOf('Welcome', module).add('Welcome', () => ({
  render: h => h(Welcome),
}));

storiesOf('App', module).add('App', () => ({
  render: h => h(App),
}));

storiesOf('Button', module)
  .addDecorator(Centered)
  // Works if Vue.component is called in the config.js in .storybook
  .add('rounded', () => ({
    template: '<my-button :rounded="true">A Button with rounded edges</my-button>',
  }))
  .add('square', () => ({
    template: '<my-button :rounded="false">A Button with square edges</my-button>',
  }));

storiesOf('Method for rendering Vue', module)
  .add('render', () => ({
    render: h => h('div', ['renders a div with some text in it..']),
  }))
  .add('render + component', () => ({
    render(h) {
      return h(MyButton, { props: { color: 'pink' } }, ['renders component: MyButton']);
    },
  }))
  .add('template', () => ({
    template: `

A template

rendered in vue in storybook

`,
  }))
  .add('template + component', () => ({
    components: { MyButton },
    template: '<my-button>MyButton rendered in a template</my-button>',
  }))
  .add('template + methods', () => ({
    components: { MyButton },
    template: `

        Clicking the button will navigate to another story using the 'addon-links'

        <my-button :rounded="true" :handle-click="action">MyButton rendered in a template + props & methods</my-button>

`,
    methods: {
      action: linkTo('Button'),
    },
  }))
  .add('vuex + actions', () => ({
    components: { MyButton },
    template: '<my-button :handle-click="log">with vuex: {{ $store.state.count }}</my-button>',
    store: new Vuex.Store({
      state: { count: 0 },
      mutations: {
        increment(state) {
          state.count += 1; // eslint-disable-line
          action('vuex state')(state);
        },
      },
    }),
    methods: {
      log() {
        this.$store.commit('increment');
      },
    },
  }))
  .add('whatever you want', () => ({
    components: { MyButton },
    template:
      '<my-button :handle-click="log">with awesomeness: {{ $store.state.count }}</my-button>',
    store: new Vuex.Store({
      state: { count: 0 },
      mutations: {
        increment(state) {
          state.count += 1; // eslint-disable-line
          action('vuex state')(state);
        },
      },
    }),
    methods: {
      log() {
        this.$store.commit('increment');
      },
    },
  }))
  .add('pre-registered component', () => ({
    /* By pre-registering component in config.js,
     * the need to register all components with each story is removed.
     * You'll only need the template */
    template: `

        This component was pre-registered in .storybook/config.js

        <my-button>MyButton rendered in a template</my-button>

`,
  }));

storiesOf('Decorator for Vue', module)
  .addDecorator(story => {
    // Decorated with story function
    const WrapButton = story();
    return {
      components: { WrapButton },
      template: '
<wrap-button/>
',
      data() {
        return { borderStyle: 'medium solid red' };
      },
    };
  })
  .addDecorator(() => ({
    // Decorated with `story` component
    template: '
<story/>
',
    data() {
      return {
        borderStyle: 'medium solid blue',
      };
    },
  }))
  .add('template', () => ({
    template: '<my-button>MyButton with template</my-button>',
  }))
  .add('render', () => ({
    render(h) {
      return h(MyButton, { props: { color: 'pink' } }, ['renders component: MyButton']);
    },
  }));

storiesOf('Addon Actions', module)
  .add('Action only', () => ({
    template: '<my-button :handle-click="log">Click me to log the action</my-button>',
    methods: {
      log: action('log1'),
    },
  }))
  .add('Action and method', () => ({
    template: '<my-button :handle-click="log">Click me to log the action</my-button>',
    methods: {
      log: e => {
        e.preventDefault();
        action('log2')(e.target);
      },
    },
  }));

storiesOf('Addon Notes', module)
  .add(
    'Simple note',
    withNotes({ text: 'My notes on some bold text' })(() => ({
      template:
        '
Etiam vulputate elit eu venenatis eleifend. Duis nec lectus augue. Morbi egestas diam sed vulputate mollis. Fusce egestas pretium vehicula. Integer sed neque diam. Donec consectetur velit vitae enim varius, ut placerat arcu imperdiet. Praesent sed faucibus arcu. Nullam sit amet nibh a enim eleifend rhoncus. Donec pretium elementum leo at fermentum. Nulla sollicitudin, mauris quis semper tempus, sem metus tristique diam, efficitur pulvinar mi urna id urna.
',
    }))
  )
  .add(
    'Note with HTML',
    withNotes({
      notes: `

My notes on emojies

      It's not all that important to be honest, but..

      Emojis are great, I love emojis, in fact I like using them in my Component notes too! 😇
    `,
    })(() => ({
      template: '
🤔😳😯😮
😄😩😓😱
🤓😑😶😊
',
    }))
  );

storiesOf('Addon Knobs', module)
  .addDecorator(withKnobs)
  .add('Simple', () => {
    const name = text('Name', 'John Doe');
    const age = number('Age', 44);
    const content = `I am ${name} and I'm ${age} years old.`;

    return {
      template: `<div>${content}</div>`,
    };
  })
  .add('All knobs', () => {
    const name = text('Name', 'Jane');
    const stock = number('Stock', 20, {
      range: true,
      min: 0,
      max: 30,
      step: 5,
    });
    const fruits = {
      apples: 'Apple',
      bananas: 'Banana',
      cherries: 'Cherry',
    };
    const fruit = select('Fruit', fruits, 'apple');
    const price = number('Price', 2.25);

    const colour = color('Border', 'deeppink');
    const today = date('Today', new Date('Jan 20 2017'));
    const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
    const nice = boolean('Nice', true);

    const stockMessage = stock
      ? `I have a stock of ${stock} ${fruit}, costing $${price} each.`
      : `I'm out of ${fruit}${nice ? ', Sorry!' : '.'}`;
    const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';

    return {
      template: `

My name is ${name},

today is ${new Date(today).toLocaleDateString()}

${stockMessage}

Also, I have:

              ${items.map(item => `<li key=${item}>${item}</li>`).join('')}

${salutation}

        `,
    };
  });
<commit_msg>Fixing typo on VueJS withNotes Example<commit_after>import Vuex from 'vuex';
import { storiesOf } from '@storybook/vue';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { withNotes } from '@storybook/addon-notes';
import {
  withKnobs,
  text,
  number,
  boolean,
  array,
  select,
  color,
  date,
} from '@storybook/addon-knobs';
import Centered from '@storybook/addon-centered';

import MyButton from './Button.vue';
import Welcome from './Welcome.vue';
import App from '../App.vue';

storiesOf('Welcome', module).add('Welcome', () => ({
  render: h => h(Welcome),
}));

storiesOf('App', module).add('App', () => ({
  render: h => h(App),
}));

storiesOf('Button', module)
  .addDecorator(Centered)
  // Works if Vue.component is called in the config.js in .storybook
  .add('rounded', () => ({
    template: '<my-button :rounded="true">A Button with rounded edges</my-button>',
  }))
  .add('square', () => ({
    template: '<my-button :rounded="false">A Button with square edges</my-button>',
  }));

storiesOf('Method for rendering Vue', module)
  .add('render', () => ({
    render: h => h('div', ['renders a div with some text in it..']),
  }))
  .add('render + component', () => ({
    render(h) {
      return h(MyButton, { props: { color: 'pink' } }, ['renders component: MyButton']);
    },
  }))
  .add('template', () => ({
    template: `

A template

rendered in vue in storybook

`,
  }))
  .add('template + component', () => ({
    components: { MyButton },
    template: '<my-button>MyButton rendered in a template</my-button>',
  }))
  .add('template + methods', () => ({
    components: { MyButton },
    template: `

        Clicking the button will navigate to another story using the 'addon-links'

        <my-button :rounded="true" :handle-click="action">MyButton rendered in a template + props & methods</my-button>

`,
    methods: {
      action: linkTo('Button'),
    },
  }))
  .add('vuex + actions', () => ({
    components: { MyButton },
    template: '<my-button :handle-click="log">with vuex: {{ $store.state.count }}</my-button>',
    store: new Vuex.Store({
      state: { count: 0 },
      mutations: {
        increment(state) {
          state.count += 1; // eslint-disable-line
          action('vuex state')(state);
        },
      },
    }),
    methods: {
      log() {
        this.$store.commit('increment');
      },
    },
  }))
  .add('whatever you want', () => ({
    components: { MyButton },
    template:
      '<my-button :handle-click="log">with awesomeness: {{ $store.state.count }}</my-button>',
    store: new Vuex.Store({
      state: { count: 0 },
      mutations: {
        increment(state) {
          state.count += 1; // eslint-disable-line
          action('vuex state')(state);
        },
      },
    }),
    methods: {
      log() {
        this.$store.commit('increment');
      },
    },
  }))
  .add('pre-registered component', () => ({
    /* By pre-registering component in config.js,
     * the need to register all components with each story is removed.
     * You'll only need the template */
    template: `

        This component was pre-registered in .storybook/config.js

        <my-button>MyButton rendered in a template</my-button>

`,
  }));

storiesOf('Decorator for Vue', module)
  .addDecorator(story => {
    // Decorated with story function
    const WrapButton = story();
    return {
      components: { WrapButton },
      template: '
<wrap-button/>
',
      data() {
        return { borderStyle: 'medium solid red' };
      },
    };
  })
  .addDecorator(() => ({
    // Decorated with `story` component
    template: '
<story/>
',
    data() {
      return {
        borderStyle: 'medium solid blue',
      };
    },
  }))
  .add('template', () => ({
    template: '<my-button>MyButton with template</my-button>',
  }))
  .add('render', () => ({
    render(h) {
      return h(MyButton, { props: { color: 'pink' } }, ['renders component: MyButton']);
    },
  }));

storiesOf('Addon Actions', module)
  .add('Action only', () => ({
    template: '<my-button :handle-click="log">Click me to log the action</my-button>',
    methods: {
      log: action('log1'),
    },
  }))
  .add('Action and method', () => ({
    template: '<my-button :handle-click="log">Click me to log the action</my-button>',
    methods: {
      log: e => {
        e.preventDefault();
        action('log2')(e.target);
      },
    },
  }));

storiesOf('Addon Notes', module)
  .add(
    'Simple note',
    withNotes({ text: 'My notes on some bold text' })(() => ({
      template:
        '
Etiam vulputate elit eu venenatis eleifend. Duis nec lectus augue. Morbi egestas diam sed vulputate mollis. Fusce egestas pretium vehicula. Integer sed neque diam. Donec consectetur velit vitae enim varius, ut placerat arcu imperdiet. Praesent sed faucibus arcu. Nullam sit amet nibh a enim eleifend rhoncus. Donec pretium elementum leo at fermentum. Nulla sollicitudin, mauris quis semper tempus, sem metus tristique diam, efficitur pulvinar mi urna id urna.
',
    }))
  )
  .add(
    'Note with HTML',
    withNotes({
      text: `

My notes on emojies

      It's not all that important to be honest, but..

      Emojis are great, I love emojis, in fact I like using them in my Component notes too! 😇
    `,
    })(() => ({
      template: '
🤔😳😯😮
😄😩😓😱
🤓😑😶😊
',
    }))
  );

storiesOf('Addon Knobs', module)
  .addDecorator(withKnobs)
  .add('Simple', () => {
    const name = text('Name', 'John Doe');
    const age = number('Age', 44);
    const content = `I am ${name} and I'm ${age} years old.`;

    return {
      template: `<div>${content}</div>`,
    };
  })
  .add('All knobs', () => {
    const name = text('Name', 'Jane');
    const stock = number('Stock', 20, {
      range: true,
      min: 0,
      max: 30,
      step: 5,
    });
    const fruits = {
      apples: 'Apple',
      bananas: 'Banana',
      cherries: 'Cherry',
    };
    const fruit = select('Fruit', fruits, 'apple');
    const price = number('Price', 2.25);

    const colour = color('Border', 'deeppink');
    const today = date('Today', new Date('Jan 20 2017'));
    const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
    const nice = boolean('Nice', true);

    const stockMessage = stock
      ? `I have a stock of ${stock} ${fruit}, costing $${price} each.`
      : `I'm out of ${fruit}${nice ? ', Sorry!' : '.'}`;
    const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';

    return {
      template: `

My name is ${name},

today is ${new Date(today).toLocaleDateString()}

${stockMessage}

Also, I have:

              ${items.map(item => `<li key=${item}>${item}</li>`).join('')}

${salutation}

        `,
    };
  });
<|endoftext|>


<commit_before>
// an API status reporter

API.addRoute('status', {
  get: {
    action: function() {
      return API.status();
    }
  }
});

API.addRoute('status/check', {
  get: {
    //roleRequired: 'root',
    action: function() {
      return API.check();
    }
  }
});

API.addRoute('status/test', {
  get: {
    //roleRequired: 'root',
    action: function() {
      return API.test('API',this.queryParams.verbose);
    }
  }
});

API.status = function() {
  var ret = {
    up: {
      live:true,
      local:true,
      cluster:true,
      dev:true
    },
    accounts: {
      total: Users.count()//,
      //online: API.accounts.onlinecount()
    },
    cron: 'TODO'
  }
  try { Meteor.http.call('GET','https://api.cottagelabs.com'); } catch(err) { ret.up.live = false; }
  try { Meteor.http.call('GET','https://lapi.cottagelabs.com'); } catch(err) { ret.up.local = false; }
  try { Meteor.http.call('GET','https://dev.api.cottagelabs.com'); } catch(err) { ret.up.dev = false; }
  try {
    Meteor.http.call('GET','https://capi.cottagelabs.com');
    if (Meteor.settings.cluster && Meteor.settings.cluster.machines) {
      var cm = 0;
      for ( var m in Meteor.settings.cluster.machines) {
        try {
          Meteor.http.call('GET','http://' + Meteor.settings.cluster.machines[m] + '/api');
          cm += 1;
        } catch(err) {}
      }
      if (cm !== 0) ret.up.cluster = cm;
    }
  } catch(err) { ret.up.cluster = false; }
  // TODO if cluster is up could read the mup file then try getting each cluster machine too, and counting them
  try { ret.lantern = API.service.lantern.status(); } catch(err) { ret.lantern = false; }
  try { ret.job = API.job.status(); } catch(err) { ret.job = false; }
  try { ret.limit = API.limit.status(); } catch(err) { ret.limit = false; }
  try { ret.openaccessbutton = API.service.oab.status(); } catch(err) { ret.openaccessbutton = false; }
  try { ret.index = API.es.status(); } catch(err) { ret.index = false; }
  return ret;
};

API.test = function(trigger,verbose) {
  var tests = {passed:true,trigger:trigger};
  // could add an elasticsearch test, but a collection test won't succeed unless ES succeeds anyway
  if (API.collection && API.collection.test) {
    tests.collection = API.collection.test();
    if (tests.collection.passed && !verbose) tests.collection = {passed:true};
    tests.passed = tests.passed && tests.collection.passed;
  }
  if (API.mail && API.mail.test) {
    tests.mail = API.mail.test();
    if (tests.mail.passed && !verbose) tests.mail = {passed:true};
    tests.passed = tests.passed && tests.mail.passed;
  }
  if (API.accounts && API.accounts.test) {
    tests.accounts = API.accounts.test();
    if (tests.accounts.passed && !verbose) tests.accounts = {passed:true};
    tests.passed = tests.passed && tests.accounts.passed;
  }
  // add a job test?
  tests.service = {};
  for ( var s in API.service ) {
    if (API.service[s].test) {
      tests.service[s] = API.service[s].test();
      if (tests.service[s].passed && !verbose) tests.service[s] = {passed:true};
      tests.passed = tests.passed && tests.service[s].passed;
    }
  }
  tests.use = {};
  for ( var u in API.use ) {
    if (API.use[u].test) {
      tests.use[u] = API.use[u].test();
      if (tests.use[u].passed && !verbose) tests.use[u] = {passed:true};
      tests.passed = tests.passed && tests.use[u].passed;
    }
  }
  var notify = tests.passed ? undefined : {msg:JSON.stringify(tests,undefined,2)};
  API.log({msg:'Completed testing',tests:tests,notify:notify});
  return tests;
}

API.check = function() {
  // for every use endpoint, send a request and check that the response looks like some stored known response
  // first check is, do we still get an answer back? and perhaps how long did it take?
  // check all params exist, look for new ones, look for different values, and give details of difference
  var check = {
    status: 'success,change,error',
    check:{
    }
  };
  // TODO some sort of overall analysis to determine what overall status should be
  // if overall status is not success, email sysadmin with details
  return check;
}

// could add a cron to run checks and tests every day and email a sysadmin

<commit_msg>tests update<commit_after>
// an API status reporter

API.addRoute('status', {
  get: {
    action: function() {
      return API.status();
    }
  }
});

API.addRoute('status/check', {
  get: {
    //roleRequired: 'root',
    action: function() {
      return API.check();
    }
  }
});

API.addRoute('status/test', {
  get: {
    //roleRequired: 'root',
    action: function() {
      return API.test('API',this.queryParams.verbose);
    }
  }
});

API.status = function() {
  var ret = {
    up: {
      live:true,
      local:true,
      cluster:true,
      dev:true
    },
    accounts: {
      total: Users.count()//,
      //online: API.accounts.onlinecount()
    },
    cron: 'TODO'
  }
  try { HTTP.call('GET','https://api.cottagelabs.com'); } catch(err) { ret.up.live = false; }
  try { HTTP.call('GET','https://lapi.cottagelabs.com'); } catch(err) { ret.up.local = false; }
  try { HTTP.call('GET','https://dev.api.cottagelabs.com'); } catch(err) { ret.up.dev = false; }
  try {
    HTTP.call('GET','https://capi.cottagelabs.com');
    if (API.settings.cluster && API.settings.cluster.machines) {
      var cm = 0;
      for ( var m in API.settings.cluster.machines) {
        try {
          HTTP.call('GET','http://' + API.settings.cluster.machines[m] + '/api');
          cm += 1;
        } catch(err) {}
      }
      if (cm !== 0) ret.up.cluster = cm;
    }
  } catch(err) { ret.up.cluster = false; }
  // TODO if cluster is up could read the mup file then try getting each cluster machine too, and counting them
  try { ret.lantern = API.service.lantern.status(); } catch(err) { ret.lantern = false; }
  try { ret.job = API.job.status(); } catch(err) { ret.job = false; }
  try { ret.limit = API.limit.status(); } catch(err) { ret.limit = false; }
  try { ret.openaccessbutton = API.service.oab.status(); } catch(err) { ret.openaccessbutton = false; }
  try { ret.index = API.es.status(); } catch(err) { ret.index = false; }
  return ret;
};

API.test = function(trigger,verbose) {
  var tests = {passed:true,trigger:trigger};
  // could add an elasticsearch test, but a collection test won't succeed unless ES succeeds anyway
  if (API.collection && API.collection.test) {
    tests.collection = API.collection.test();
    if (tests.collection.passed && !verbose) tests.collection = {passed:true};
    tests.passed = tests.passed && tests.collection.passed;
  }
  if (API.mail && API.mail.test) {
    tests.mail = API.mail.test();
    if (tests.mail.passed && !verbose) tests.mail = {passed:true};
    tests.passed = tests.passed && tests.mail.passed;
  }
  if (API.accounts && API.accounts.test) {
    tests.accounts = API.accounts.test();
    if (tests.accounts.passed && !verbose) tests.accounts = {passed:true};
    tests.passed = tests.passed && tests.accounts.passed;
  }
  // add a job test?
  tests.service = {};
  for ( var s in API.service ) {
    if (API.service[s].test) {
      tests.service[s] = API.service[s].test();
      if (tests.service[s].passed && !verbose) tests.service[s] = {passed:true};
      tests.passed = tests.passed && tests.service[s].passed;
    }
  }
  tests.use = {};
  for ( var u in API.use ) {
    if (API.use[u].test) {
      tests.use[u] = API.use[u].test();
      if (tests.use[u].passed && !verbose) tests.use[u] = {passed:true};
      tests.passed = tests.passed && tests.use[u].passed;
    }
  }
  var notify = tests.passed ? undefined : {msg:JSON.stringify(tests,undefined,2)};
  API.log({msg:'Completed testing',tests:tests,notify:notify});
  return tests;
}

API.check = function() {
  // for every use endpoint, send a request and check that the response looks like some stored known response
  // first check is, do we still get an answer back? and perhaps how long did it take?
  // check all params exist, look for new ones, look for different values, and give details of difference
  var check = {
    status: 'success,change,error',
    check:{
    }
  };
  // TODO some sort of overall analysis to determine what overall status should be
  // if overall status is not success, email sysadmin with details
  return check;
}

// could add a cron to run checks and tests every day and email a sysadmin

<|endoftext|>


<commit_before>'use strict';

import { isDef, string } from 'metal';
import parse from './parse';
import { MultiMap } from 'metal-structs';

var parseFn_ = parse;

class Uri {

	/**
	 * This class contains setters and getters for the parts of the URI.
	 * The following figure displays an example URIs and their component parts.
	 *
	 *                                  path
	 *	                             ┌───┴────┐
	 *	  abc://example.com:123/path/data?key=value#fragid1
	 *	  └┬┘   └────┬────┘ └┬┘           └───┬───┘ └──┬──┘
	 * protocol  hostname  port            search    hash
	 *          └──────┬───────┘
	 *                host
	 *
	 * @param {*=} opt_uri Optional string URI to parse
	 * @constructor
	 */
	constructor(opt_uri = '') {
		this.url = Uri.parse(this.maybeAddProtocolAndHostname_(opt_uri));
	}

	/**
	 * Adds parameters to uri from a MultiMap as source.
	 * @param {MultiMap} multimap The MultiMap containing the
	 *   parameters.
	 * @protected
	 * @chainable
	 */
	addParametersFromMultiMap(multimap) {
		multimap.names().forEach((name) => {
			multimap.getAll(name).forEach((value) => {
				this.addParameterValue(name, value);
			});
		});
		return this;
	}

	/**
	 * Adds the value of the named query parameters.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value. Will be explicitly casted to String.
	 * @chainable
	 */
	addParameterValue(name, value) {
		this.ensureQueryInitialized_();
		if (isDef(value)) {
			value = String(value);
		}
		this.query.add(name, value);
		return this;
	}

	/**
	 * Adds the values of the named query parameter.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	addParameterValues(name, values) {
		values.forEach((value) => this.addParameterValue(name, value));
		return this;
	}

	/**
	 * Ensures query internal map is initialized and synced with initial value
	 * extracted from URI search part.
	 * @protected
	 */
	ensureQueryInitialized_() {
		if (this.query) {
			return;
		}
		this.query = new MultiMap();
		var search = this.url.search;
		if (search) {
			search.substring(1).split('&').forEach((param) => {
				var [key, value] = param.split('=');
				if (isDef(value)) {
					value = Uri.urlDecode(value);
				}
				this.addParameterValue(key, value);
			});
		}
	}

	/**
	 * Gets the hash part of uri.
	 * @return {string}
	 */
	getHash() {
		return this.url.hash || '';
	}

	/**
	 * Gets the host part of uri. E.g. [hostname]:[port].
	 * @return {string}
	 */
	getHost() {
		var host = this.getHostname();
		if (host) {
			var port = this.getPort();
			if (port && port !== '80') {
				host += ':' + port;
			}
		}
		return host;
	}

	/**
	 * Gets the hostname part of uri without protocol and port.
	 * @return {string}
	 */
	getHostname() {
		var hostname = this.url.hostname;
		if (hostname === Uri.HOSTNAME_PLACEHOLDER) {
			return '';
		}
		return hostname;
	}

	/**
	 * Gets the origin part of uri. E.g. http://[hostname]:[port].
	 * @return {string}
	 */
	getOrigin() {
		var host = this.getHost();
		if (host) {
			return this.getProtocol() + '//' + host;
		}
		return '';
	}

	/**
	 * Returns the first value for a given parameter or undefined if the given
	 * parameter name does not appear in the query string.
	 * @param {string} paramName Unescaped parameter name.
	 * @return {string|undefined} The first value for a given parameter or
	 *   undefined if the given parameter name does not appear in the query
	 *   string.
	 */
	getParameterValue(name) {
		this.ensureQueryInitialized_();
		return this.query.get(name);
	}

	/**
	 * Returns the values for a given parameter as a list of decoded
	 * query parameter values.
	 * @param {string} name The parameter to get values for.
	 * @return {!Array<?>} The values for a given parameter as a list of decoded
	 *   query parameter values.
	 */
	getParameterValues(name) {
		this.ensureQueryInitialized_();
		return this.query.getAll(name);
	}

	/**
	 * Returns the names of the parameters.
	 * @return {!Array<string>} The names for the parameters as a list of
	 *   strings.
	 */
	getParameterNames() {
		this.ensureQueryInitialized_();
		return this.query.names();
	}

	/**
	 * Gets the function currently being used to parse URIs.
	 * @return {!function()}
	 */
	static getParseFn() {
		return parseFn_;
	}

	/**
	 * Gets the pathname part of uri.
	 * @return {string}
	 */
	getPathname() {
		return this.url.pathname;
	}

	/**
	 * Gets the port number part of uri as string.
	 * @return {string}
	 */
	getPort() {
		return this.url.port;
	}

	/**
	 * Gets the protocol part of uri. E.g. http:.
	 * @return {string}
	 */
	getProtocol() {
		return this.url.protocol;
	}

	/**
	 * Gets the search part of uri. Search value is retrieved from query
	 * parameters.
	 * @return {string}
	 */
	getSearch() {
		var search = '';
		var querystring = '';
		this.getParameterNames().forEach((name) => {
			this.getParameterValues(name).forEach((value) => {
				querystring += name;
				if (isDef(value)) {
					querystring += '=' + encodeURIComponent(value);
				}
				querystring += '&';
			});
		});
		querystring = querystring.slice(0, -1);
		if (querystring) {
			search += '?' + querystring;
		}
		return search;
	}

	/**
	 * Checks if uri contains the parameter.
	 * @param {string} name
	 * @return {boolean}
	 */
	hasParameter(name) {
		this.ensureQueryInitialized_();
		return this.query.contains(name);
	}

	/**
	 * Makes this URL unique by adding a random param to it. Useful for avoiding
	 * cache.
	 */
	makeUnique() {
		this.setParameterValue(Uri.RANDOM_PARAM, string.getRandomString());
		return this;
	}

	/**
	 * Maybe adds protocol and a hostname placeholder on a parial URI if needed.
	 * Relevent for compatibility with URL native object.
	 * @param {string=} opt_uri
	 * @return {string} URI with protocol and hostname placeholder.
	 */
	maybeAddProtocolAndHostname_(opt_uri) {
		var url = opt_uri;
		if (opt_uri.indexOf('://') === -1 &&
			opt_uri.indexOf('javascript:') !== 0) { // jshint ignore:line

			url = Uri.DEFAULT_PROTOCOL;
			if (opt_uri[0] !== '/' || opt_uri[1] !== '/') {
				url += '//';
			}

			switch (opt_uri.charAt(0)) {
				case '.':
				case '?':
				case '#':
					url += Uri.HOSTNAME_PLACEHOLDER;
					url += '/';
					url += opt_uri;
					break;
				case '':
				case '/':
					if (opt_uri[1] !== '/') {
						url += Uri.HOSTNAME_PLACEHOLDER;
					}
					url += opt_uri;
					break;
				default:
					url += opt_uri;
			}
		}
		return url;
	}

	/**
	 * Normalizes the parsed object to be in the expected standard.
	 * @param {!Object}
	 */
	static normalizeObject(parsed) {
		var length = parsed.pathname ? parsed.pathname.length : 0;
		if (length > 1 && parsed.pathname[length - 1] === '/') {
			parsed.pathname = parsed.pathname.substr(0, length - 1);
		}
		return parsed;
	}

	/**
	 * Parses the given uri string into an object.
	 * @param {*=} opt_uri Optional string URI to parse
	 */
	static parse(opt_uri) {
		return Uri.normalizeObject(parseFn_(opt_uri));
	}

	/**
	 * Removes the named query parameter.
	 * @param {string} name The parameter to remove.
	 * @chainable
	 */
	removeParameter(name) {
		this.ensureQueryInitialized_();
		this.query.remove(name);
		return this;
	}

	/**
	 * Removes uniqueness parameter of the uri.
	 * @chainable
	 */
	removeUnique() {
		this.removeParameter(Uri.RANDOM_PARAM);
		return this;
	}

	/**
	 * Sets the hash.
	 * @param {string} hash
	 * @chainable
	 */
	setHash(hash) {
		this.url.hash = hash;
		return this;
	}

	/**
	 * Sets the hostname.
	 * @param {string} hostname
	 * @chainable
	 */
	setHostname(hostname) {
		this.url.hostname = hostname;
		return this;
	}

	/**
	 * Sets the value of the named query parameters, clearing previous values
	 * for that key.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	setParameterValue(name, value) {
		this.removeParameter(name);
		this.addParameterValue(name, value);
		return this;
	}

	/**
	 * Sets the values of the named query parameters, clearing previous values
	 * for that key.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	setParameterValues(name, values) {
		this.removeParameter(name);
		values.forEach((value) => this.addParameterValue(name, value));
		return this;
	}

	/**
	 * Sets the pathname.
	 * @param {string} pathname
	 * @chainable
	 */
	setPathname(pathname) {
		this.url.pathname = pathname;
		return this;
	}

	/**
	 * Sets the port number.
	 * @param {*} port Port number.
	 * @chainable
	 */
	setPort(port) {
		this.url.port = port;
		return this;
	}

	/**
	 * Sets the function that will be used for parsing the original string uri
	 * into an object.
	 * @param {!function()} parseFn
	 */
	static setParseFn(parseFn) {
		parseFn_ = parseFn;
	}

	/**
	 * Sets the protocol. If missing http: is used as default.
	 * @param {string} protocol
	 * @chainable
	 */
	setProtocol(protocol) {
		this.url.protocol = protocol;
		if (this.url.protocol[this.url.protocol.length - 1] !== ':') {
			this.url.protocol += ':';
		}
		return this;
	}

	/**
	 * @return {string} The string form of the url.
	 * @override
	 */
	toString() {
		var href = '';
		var host = this.getHost();
		if (host) {
			href += this.getProtocol() + '//';
		}
		href += host + this.getPathname() + this.getSearch() + this.getHash();
		return href;
	}

	/**
	 * Joins the given paths.
	 * @param {string} basePath
	 * @param {...string} ...paths Any number of paths to be joined with the base url.
	 * @static
	 */
	static joinPaths(basePath, ...paths) {
		if (basePath.charAt(basePath.length - 1) === '/') {
			basePath = basePath.substring(0, basePath.length - 1);
		}
		paths = paths.map(path => path.charAt(0) === '/' ? path.substring(1) : path);
		return [basePath].concat(paths).join('/').replace(/\/$/, '');
	}

	/**
	 * URL-decodes the string. We need to specially handle '+'s because
	 * the javascript library doesn't convert them to spaces.
	 * @param {string} str The string to url decode.
	 * @return {string} The decoded {@code str}.
	 */
	static urlDecode(str) {
		return decodeURIComponent(str.replace(/\+/g, ' '));
	}

}

/**
 * Default protocol value.
 * @type {string}
 * @default http:
 * @static
 */
Uri.DEFAULT_PROTOCOL = (typeof require === 'undefined') &&
	(typeof window !== 'undefined') ?
	window.location.protocol : 'http:';

/**
 * Hostname placeholder. Relevant to internal usage only.
 * @type {string}
 * @static
 */
Uri.HOSTNAME_PLACEHOLDER = 'hostname' + Date.now();

/**
 * Name used by the param generated by `makeUnique`.
 * @type {string}
 * @static
 */
Uri.RANDOM_PARAM = 'zx';

export default Uri;
<commit_msg>Wraps shouldInheritProtocol to avoid packing issues<commit_after>'use strict';

import { isDef, string } from 'metal';
import parse from './parse';
import { MultiMap } from 'metal-structs';

var parseFn_ = parse;

class Uri {

	/**
	 * This class contains setters and getters for the parts of the URI.
	 * The following figure displays an example URIs and their component parts.
	 *
	 *                                  path
	 *	                             ┌───┴────┐
	 *	  abc://example.com:123/path/data?key=value#fragid1
	 *	  └┬┘   └────┬────┘ └┬┘           └───┬───┘ └──┬──┘
	 * protocol  hostname  port            search    hash
	 *          └──────┬───────┘
	 *                host
	 *
	 * @param {*=} opt_uri Optional string URI to parse
	 * @constructor
	 */
	constructor(opt_uri = '') {
		this.url = Uri.parse(this.maybeAddProtocolAndHostname_(opt_uri));
	}

	/**
	 * Adds parameters to uri from a MultiMap as source.
	 * @param {MultiMap} multimap The MultiMap containing the
	 *   parameters.
	 * @protected
	 * @chainable
	 */
	addParametersFromMultiMap(multimap) {
		multimap.names().forEach((name) => {
			multimap.getAll(name).forEach((value) => {
				this.addParameterValue(name, value);
			});
		});
		return this;
	}

	/**
	 * Adds the value of the named query parameters.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value. Will be explicitly casted to String.
	 * @chainable
	 */
	addParameterValue(name, value) {
		this.ensureQueryInitialized_();
		if (isDef(value)) {
			value = String(value);
		}
		this.query.add(name, value);
		return this;
	}

	/**
	 * Adds the values of the named query parameter.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	addParameterValues(name, values) {
		values.forEach((value) => this.addParameterValue(name, value));
		return this;
	}

	/**
	 * Ensures query internal map is initialized and synced with initial value
	 * extracted from URI search part.
	 * @protected
	 */
	ensureQueryInitialized_() {
		if (this.query) {
			return;
		}
		this.query = new MultiMap();
		var search = this.url.search;
		if (search) {
			search.substring(1).split('&').forEach((param) => {
				var [key, value] = param.split('=');
				if (isDef(value)) {
					value = Uri.urlDecode(value);
				}
				this.addParameterValue(key, value);
			});
		}
	}

	/**
	 * Gets the hash part of uri.
	 * @return {string}
	 */
	getHash() {
		return this.url.hash || '';
	}

	/**
	 * Gets the host part of uri. E.g. [hostname]:[port].
	 * @return {string}
	 */
	getHost() {
		var host = this.getHostname();
		if (host) {
			var port = this.getPort();
			if (port && port !== '80') {
				host += ':' + port;
			}
		}
		return host;
	}

	/**
	 * Gets the hostname part of uri without protocol and port.
	 * @return {string}
	 */
	getHostname() {
		var hostname = this.url.hostname;
		if (hostname === Uri.HOSTNAME_PLACEHOLDER) {
			return '';
		}
		return hostname;
	}

	/**
	 * Gets the origin part of uri. E.g. http://[hostname]:[port].
	 * @return {string}
	 */
	getOrigin() {
		var host = this.getHost();
		if (host) {
			return this.getProtocol() + '//' + host;
		}
		return '';
	}

	/**
	 * Returns the first value for a given parameter or undefined if the given
	 * parameter name does not appear in the query string.
	 * @param {string} paramName Unescaped parameter name.
	 * @return {string|undefined} The first value for a given parameter or
	 *   undefined if the given parameter name does not appear in the query
	 *   string.
	 */
	getParameterValue(name) {
		this.ensureQueryInitialized_();
		return this.query.get(name);
	}

	/**
	 * Returns the values for a given parameter as a list of decoded
	 * query parameter values.
	 * @param {string} name The parameter to get values for.
	 * @return {!Array<?>} The values for a given parameter as a list of decoded
	 *   query parameter values.
	 */
	getParameterValues(name) {
		this.ensureQueryInitialized_();
		return this.query.getAll(name);
	}

	/**
	 * Returns the names of the parameters.
	 * @return {!Array<string>} The names for the parameters as a list of
	 *   strings.
	 */
	getParameterNames() {
		this.ensureQueryInitialized_();
		return this.query.names();
	}

	/**
	 * Gets the function currently being used to parse URIs.
	 * @return {!function()}
	 */
	static getParseFn() {
		return parseFn_;
	}

	/**
	 * Gets the pathname part of uri.
	 * @return {string}
	 */
	getPathname() {
		return this.url.pathname;
	}

	/**
	 * Gets the port number part of uri as string.
	 * @return {string}
	 */
	getPort() {
		return this.url.port;
	}

	/**
	 * Gets the protocol part of uri. E.g. http:.
	 * @return {string}
	 */
	getProtocol() {
		return this.url.protocol;
	}

	/**
	 * Gets the search part of uri. Search value is retrieved from query
	 * parameters.
	 * @return {string}
	 */
	getSearch() {
		var search = '';
		var querystring = '';
		this.getParameterNames().forEach((name) => {
			this.getParameterValues(name).forEach((value) => {
				querystring += name;
				if (isDef(value)) {
					querystring += '=' + encodeURIComponent(value);
				}
				querystring += '&';
			});
		});
		querystring = querystring.slice(0, -1);
		if (querystring) {
			search += '?' + querystring;
		}
		return search;
	}

	/**
	 * Checks if uri contains the parameter.
	 * @param {string} name
	 * @return {boolean}
	 */
	hasParameter(name) {
		this.ensureQueryInitialized_();
		return this.query.contains(name);
	}

	/**
	 * Makes this URL unique by adding a random param to it. Useful for avoiding
	 * cache.
	 */
	makeUnique() {
		this.setParameterValue(Uri.RANDOM_PARAM, string.getRandomString());
		return this;
	}

	/**
	 * Maybe adds protocol and a hostname placeholder on a parial URI if needed.
	 * Relevent for compatibility with URL native object.
	 * @param {string=} opt_uri
	 * @return {string} URI with protocol and hostname placeholder.
	 */
	maybeAddProtocolAndHostname_(opt_uri) {
		var url = opt_uri;
		if (opt_uri.indexOf('://') === -1 &&
			opt_uri.indexOf('javascript:') !== 0) { // jshint ignore:line

			url = Uri.DEFAULT_PROTOCOL;
			if (opt_uri[0] !== '/' || opt_uri[1] !== '/') {
				url += '//';
			}

			switch (opt_uri.charAt(0)) {
				case '.':
				case '?':
				case '#':
					url += Uri.HOSTNAME_PLACEHOLDER;
					url += '/';
					url += opt_uri;
					break;
				case '':
				case '/':
					if (opt_uri[1] !== '/') {
						url += Uri.HOSTNAME_PLACEHOLDER;
					}
					url += opt_uri;
					break;
				default:
					url += opt_uri;
			}
		}
		return url;
	}

	/**
	 * Normalizes the parsed object to be in the expected standard.
	 * @param {!Object}
	 */
	static normalizeObject(parsed) {
		var length = parsed.pathname ? parsed.pathname.length : 0;
		if (length > 1 && parsed.pathname[length - 1] === '/') {
			parsed.pathname = parsed.pathname.substr(0, length - 1);
		}
		return parsed;
	}

	/**
	 * Parses the given uri string into an object.
	 * @param {*=} opt_uri Optional string URI to parse
	 */
	static parse(opt_uri) {
		return Uri.normalizeObject(parseFn_(opt_uri));
	}

	/**
	 * Removes the named query parameter.
	 * @param {string} name The parameter to remove.
	 * @chainable
	 */
	removeParameter(name) {
		this.ensureQueryInitialized_();
		this.query.remove(name);
		return this;
	}

	/**
	 * Removes uniqueness parameter of the uri.
	 * @chainable
	 */
	removeUnique() {
		this.removeParameter(Uri.RANDOM_PARAM);
		return this;
	}

	/**
	 * Sets the hash.
	 * @param {string} hash
	 * @chainable
	 */
	setHash(hash) {
		this.url.hash = hash;
		return this;
	}

	/**
	 * Sets the hostname.
	 * @param {string} hostname
	 * @chainable
	 */
	setHostname(hostname) {
		this.url.hostname = hostname;
		return this;
	}

	/**
	 * Sets the value of the named query parameters, clearing previous values
	 * for that key.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	setParameterValue(name, value) {
		this.removeParameter(name);
		this.addParameterValue(name, value);
		return this;
	}

	/**
	 * Sets the values of the named query parameters, clearing previous values
	 * for that key.
	 * @param {string} key The parameter to set.
	 * @param {*} value The new value.
	 * @chainable
	 */
	setParameterValues(name, values) {
		this.removeParameter(name);
		values.forEach((value) => this.addParameterValue(name, value));
		return this;
	}

	/**
	 * Sets the pathname.
	 * @param {string} pathname
	 * @chainable
	 */
	setPathname(pathname) {
		this.url.pathname = pathname;
		return this;
	}

	/**
	 * Sets the port number.
	 * @param {*} port Port number.
	 * @chainable
	 */
	setPort(port) {
		this.url.port = port;
		return this;
	}

	/**
	 * Sets the function that will be used for parsing the original string uri
	 * into an object.
	 * @param {!function()} parseFn
	 */
	static setParseFn(parseFn) {
		parseFn_ = parseFn;
	}

	/**
	 * Sets the protocol. If missing http: is used as default.
	 * @param {string} protocol
	 * @chainable
	 */
	setProtocol(protocol) {
		this.url.protocol = protocol;
		if (this.url.protocol[this.url.protocol.length - 1] !== ':') {
			this.url.protocol += ':';
		}
		return this;
	}

	/**
	 * @return {string} The string form of the url.
	 * @override
	 */
	toString() {
		var href = '';
		var host = this.getHost();
		if (host) {
			href += this.getProtocol() + '//';
		}
		href += host + this.getPathname() + this.getSearch() + this.getHash();
		return href;
	}

	/**
	 * Joins the given paths.
	 * @param {string} basePath
	 * @param {...string} ...paths Any number of paths to be joined with the base url.
	 * @static
	 */
	static joinPaths(basePath, ...paths) {
		if (basePath.charAt(basePath.length - 1) === '/') {
			basePath = basePath.substring(0, basePath.length - 1);
		}
		paths = paths.map(path => path.charAt(0) === '/' ? path.substring(1) : path);
		return [basePath].concat(paths).join('/').replace(/\/$/, '');
	}

	/**
	 * URL-decodes the string. We need to specially handle '+'s because
	 * the javascript library doesn't convert them to spaces.
	 * @param {string} str The string to url decode.
	 * @return {string} The decoded {@code str}.
	 */
	static urlDecode(str) {
		return decodeURIComponent(str.replace(/\+/g, ' '));
	}

}

/**
 * Default protocol value.
 * @type {string}
 * @default http:
 * @static
 */
const shouldInheritProtocol = () =>
	(typeof require === 'undefined' && typeof window !== 'undefined');

Uri.DEFAULT_PROTOCOL = shouldInheritProtocol() ? window.location.protocol : 'http:';

/**
 * Hostname placeholder. Relevant to internal usage only.
 * @type {string}
 * @static
 */
Uri.HOSTNAME_PLACEHOLDER = 'hostname' + Date.now();

/**
 * Name used by the param generated by `makeUnique`.
 * @type {string}
 * @static
 */
Uri.RANDOM_PARAM = 'zx';

export default Uri;
<|endoftext|>


<commit_before>var root = this,
    jQuery = root.jQuery,
    Backbone = root.Backbone,
    _ = root._;

var VIE = root.VIE = function(config) {
    this.config = (config) ? config : {};
    this.services = {};
    this.entities = new this.Collection();

    this.Entity.prototype.entities = this.entities;
    this.entities.vie = this;
    this.Entity.prototype.entityCollection = this.Collection;
    this.Entity.prototype.vie = this;

    this.defaultProxyUrl = (this.config.defaultProxyUrl) ? this.config.defaultProxyUrl : "../utils/proxy/proxy.php";

    this.Namespaces.prototype.vie = this;
    this.namespaces = new this.Namespaces(
        (this.config.defaultNamespace) ? this.config.defaultNamespace : "http://ontology.vie.js/"
    );

    this.Type.prototype.vie = this;
    this.Types.prototype.vie = this;
    this.Attribute.prototype.vie = this;
    this.Attributes.prototype.vie = this;
    this.types = new this.Types();
    this.types.add("Thing");

    if (this.config.classic !== false) {
        // Load Classic API as well
        this.RDFa = new this.ClassicRDFa(this);
        this.RDFaEntities = new this.ClassicRDFaEntities(this);
        this.EntityManager = new this.ClassicEntityManager(this);

        this.cleanup = function() {
            this.entities.reset();
        };
    }
};

VIE.prototype.use = function(service, name) {
  if (!name) {
    name = service.name;
  }
  service.vie = this;
  service.name = name;
  if (service.init) {
      service.init();
  }
  this.services[name] = service;
};

VIE.prototype.service = function(name) {
  if (!this.services[name]) {
    throw "Undefined service " + name;
  }
  return this.services[name];
};

VIE.prototype.getServicesArray = function() {
  var res = [];
  _.each(this.services, function(service, i){res.push(service);});
  return res;
};

// Declaring the ..able classes
// Loadable
VIE.prototype.load = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Loadable(options);
};

// Savable
VIE.prototype.save = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Savable(options);
};

// Removable
VIE.prototype.remove = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Removable(options);
};

// Analyzable
VIE.prototype.analyze = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Analyzable(options);
};

// Findable
VIE.prototype.find = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Findable(options);
};

// bootstrap VIE with a type and attribute ontology/schema
VIE.prototype.loadSchema = function(id) {
    if (!id) {
        //TODO: load default schema
    } else {
        //TODO: try to load the given schema if available
    }
};

if(typeof exports === 'object') {
    // Running under Node.js or other CommonJS environment
    exports.VIE = VIE;

    if (!jQuery) {
        jQuery = require('jquery');
    }
    if (!Backbone) {
        Backbone = require('backbone');
    }
    if (!_) {
        _ = require('underscore')._;
    }
}
<commit_msg>Start writing the Docco docs<commit_after>//     VIE - Vienna IKS Editables
//     (c) 2011 Henri Bergius, IKS Consortium
//     VIE may be freely distributed under the MIT license.
//     For all details and documentation:
//     http://bergie.github.com/VIE/
var root = this,
    jQuery = root.jQuery,
    Backbone = root.Backbone,
    _ = root._;

var VIE = root.VIE = function(config) {
    this.config = (config) ? config : {};
    this.services = {};
    this.entities = new this.Collection();

    this.Entity.prototype.entities = this.entities;
    this.entities.vie = this;
    this.Entity.prototype.entityCollection = this.Collection;
    this.Entity.prototype.vie = this;

    this.defaultProxyUrl = (this.config.defaultProxyUrl) ? this.config.defaultProxyUrl : "../utils/proxy/proxy.php";

    this.Namespaces.prototype.vie = this;
    this.namespaces = new this.Namespaces(
        (this.config.defaultNamespace) ? this.config.defaultNamespace : "http://ontology.vie.js/"
    );

    this.Type.prototype.vie = this;
    this.Types.prototype.vie = this;
    this.Attribute.prototype.vie = this;
    this.Attributes.prototype.vie = this;
    this.types = new this.Types();
    this.types.add("Thing");

    if (this.config.classic !== false) {
        // Load Classic API as well
        this.RDFa = new this.ClassicRDFa(this);
        this.RDFaEntities = new this.ClassicRDFaEntities(this);
        this.EntityManager = new this.ClassicEntityManager(this);

        this.cleanup = function() {
            this.entities.reset();
        };
    }
};

VIE.prototype.use = function(service, name) {
  if (!name) {
    name = service.name;
  }
  service.vie = this;
  service.name = name;
  if (service.init) {
      service.init();
  }
  this.services[name] = service;
};

VIE.prototype.service = function(name) {
  if (!this.services[name]) {
    throw "Undefined service " + name;
  }
  return this.services[name];
};

VIE.prototype.getServicesArray = function() {
  var res = [];
  _.each(this.services, function(service, i){res.push(service);});
  return res;
};

// Declaring the ..able classes
// Loadable
VIE.prototype.load = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Loadable(options);
};

// Savable
VIE.prototype.save = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Savable(options);
};

// Removable
VIE.prototype.remove = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Removable(options);
};

// Analyzable
VIE.prototype.analyze = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Analyzable(options);
};

// Findable
VIE.prototype.find = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Findable(options);
};

// bootstrap VIE with a type and attribute ontology/schema
VIE.prototype.loadSchema = function(id) {
    if (!id) {
        //TODO: load default schema
    } else {
        //TODO: try to load the given schema if available
    }
};

// ## Running VIE on Node.js
//
// When VIE is running under Node.js we can use the CommonJS
// require interface to load our dependencies automatically.
//
// This means Node.js users don't need to care about dependencies
// and can just run VIE with:
//
//     var VIE = require('vie');
//
// In browser environments the dependencies have to be included
// before including VIE itself.
if(typeof exports === 'object') {
    exports.VIE = VIE;

    if (!jQuery) {
        jQuery = require('jquery');
    }
    if (!Backbone) {
        Backbone = require('backbone');
    }
    if (!_) {
        _ = require('underscore')._;
    }
}
<|endoftext|>


<commit_before>// **Zen-Shaw** is an enhanced [Emmet](http://emmet.io/)-powered `documentFragment` builder.
//
// ### Build
// 1. Install node on your machine.
// 2. Install the _Grunt-CLI_ as Root: `sudo npm install -g grunt-cli`
// 3. Issue `npm install` in project directory to get Grunt and it's dependencies
// 4. Issue `grunt` in project directory to build an uglified library.
//
// ### Issues
// Visit [our Atlassian Jira](https://jira.fork.de/browse/ZEN) instance to post issues.
//
// ### License
// Copyright (C) 2013 Fork Unstable Media GmbH
//
// Zen-Shaw is licensed within the terms of the MIT license.

var Zen = (function($) {
	'use strict';

	var dataSource,
		parser = emmet.require('abbreviationParser');;

	function each(array, fn) {
		for (var i = 0, len = array.length; i < len; i++) {
			if (fn.call(array[i], i) === false) return;
		}
	}
	function includes(haystack, needle) {
		var isIncluded = false;
		each(haystack, function () {
			return !(isIncluded = this.name === needle);
		});
		return isIncluded;
	}

	// ## Our extra portion of syntax
	// This function does the magic of implicit tag names
	function getName(properties) {
		if (properties._name) return properties._name;

		switch (properties.parent._name) {
		// example: `ul > {List item #$}*7`
		case 'ol':
		case 'ul':
			return 'li';
		// example: `table[border=1] > .row*5 > {Test}*3`
		case 'table':
			return 'tr';
		case 'tr':
			return 'td';
		// example: `select > {hallo?}*5`
		case 'select':
			return 'option';
		// example: `nav > #test-ul > {Test1} + {Test2}`
		case 'nav':
			return 'ul';
		// childs of `form` are `input`-tags, if they have a `type`-attribute. example: `form > [type=text]#username + [type=password]#password`
		case 'form':
			var isInput = includes(properties._attributes, 'type');
			return isInput ? 'input' : 'div';
		// childs of `head` are `meta`-tags, if they have a `content`-attribute. example: `head > [content="UTF-8"] + [name=viewport content="width=device-width"]`
		case 'head':
			var isMeta = includes(properties._attributes, 'content');
			return isMeta ? 'meta' : 'div';
		default:
			return 'div';
		}
	}

	function interpolate(value, counter) {
		var result = value;
		var key = result.match(/\$:([a-z]+)/i);
		var replacement = dataSource(key[1], counter - 1);
		if(typeof replacement !== 'undefined') {
			return result.replace(key[0], replacement);
		} else {
			return result.replace(key[0], "[[specify value for "+key[0]+"]]");
		}
	}

	function checkMarker(textRaw, position) {
		if(textRaw.charAt(position-1) == "\\") return "escaped";
		if(textRaw.charAt(position+1) == ":") return "token";
		return "counter";
	}

	// ## Additional content functions
	// By default, Emmet only supports plain text within curly braces
	function buildNodeText(textRaw, counter) {
		var occurrences = getIndices(textRaw),
			searchText = "",
			text = textRaw.substring(0, occurrences[0]),
			startPos = occurrences[0],
			endPos = (occurrences.length>1) ? occurrences[1] : textRaw.length,
			mode;

		for(var i=0;i<occurrences.length;i++) {

			searchText = textRaw.substring(startPos,endPos);
			mode = checkMarker(textRaw, occurrences[i]);

			// Escaping Emmet keys like `$`. Example: `span{You have to save \\$1000}`
			if(mode ==  'escaped') {
				text+=searchText;
				text=text.replace(/\\/,"");
			}
			// This parses the inputDataSource as explained at the Main-Function lower on this page
			if(mode ==  'token') {
				text+=interpolate(searchText, counter);
			}
			// [The good, old counter](http://docs.emmet.io/abbreviations/syntax/#item-numbering-) is now also working to set the elements content
			if(mode ==  'counter') {
				searchText=searchText.replace("$",counter);
				text+=searchText;
			}

			startPos = endPos;
			endPos = (occurrences.length>=i+3) ? occurrences[i+2] : textRaw.length;
		}

		return text;
	}

	function getIndices(value) {
	    var startIndex = 0;
	    var index, indices = [];
	    while ((index = value.indexOf("$", startIndex)) > -1) {
	        indices.push(index);
	        startIndex = index + 1;
	    }
	    return indices;
	}

	function buildNode(properties) {
		var name = getName(properties);
		var node = document.createElement(name);

		if (properties._text) {
			// `textContent` is not supported by IE8. [Use a shim](https://forrst.com/posts/textContent_in_IE8-eNC).
			if(properties._text.indexOf('$') < 0) {
				node.textContent = properties._text;
			} else {
				node.textContent = buildNodeText(properties._text, properties.counter);
			}
		}

		each(properties.children, function () {
			this.parent._name = name;
		});

		each(properties._attributes, function () {
			this.value = buildNodeText(this.value, properties.counter);
			node.setAttribute(this.name, this.value);
		});

		return node;
	}

	function traverseTree(base, context){
		var node = buildNode(base);
		context.appendChild(node);
		each(base.children, function() {
			traverseTree(this, node);
		});
	}

	function getterFor(data) {
		return function(key, index) {
			var value = data[key];

			if (typeof value === 'object') {
				if (value.length > index) {
					return value[index];
				} else {
					return value[index-value.length]
				}
			}

			if (typeof value === 'function') {
				return value.call(this, index);
			}

			return value;
		};
	}

	// ## The main function
	// returns `documentFragment`
	function Zen(expression, inputDataSource) {
		// The special thing about **Zen-Shaw**: Submitting data that you can use to build your DOM.
		// For example:
		//
		// ```javascript
		// Zen('ul > #test-$:b{$:i}*3', {
		// 	'b': [1, 2, 3],
		// 	'i': ['foo', 'bar', 'baz']
		// });
		// ```
		//
		// It's also possible to submit a function as inputDataSource:
		//
		// ```javascript
		// var classNames = ["foo", "bar", "baz"];
		// Zen('ul > {$:i}*3', function (key, index) {
		// 	return (key === 'i') ? classNames[index] : 'xxx';
		// });
		// ```
		//
		if(typeof(inputDataSource) == 'undefined') {
			dataSource = function() { return false }
		}
		else if (typeof(inputDataSource) !== 'function') {
			dataSource = getterFor(inputDataSource);
		} else {
			dataSource = inputDataSource;
		}

		var fragment = document.createDocumentFragment();
		var nodes = parser.parse(expression).children;
		each(nodes, function () {
			traverseTree(this, fragment);
		});
		return fragment;
	};

	// ## jQuery integration
	// The functions take the same arguments as `Zen()`
	if ($) {
		// Okay, `Zen()` is now also reachable by `$.zen()`
		$.zen = function(expression, dataSource) {
			var fragment = Zen(expression, dataSource);
			return $(fragment);
		}
		// Set the inner HTML of the given object. Example: `$('body').zen('table[border=1]>.row*5>{Test}*3');`
		$.fn.zen = function(expression, dataSource) {
			var fragment = Zen(expression, dataSource);
			return this.each(function() {
				var clone = fragment.cloneNode(true);
				this.innerHTML = '';
				this.appendChild(clone);
			});
		};
	}

	return Zen;

})(window.jQuery);
<commit_msg>added comments for documentation<commit_after>// **Zen-Shaw** is an enhanced [Emmet](http://emmet.io/)-powered `documentFragment` builder.
//
// ### Build
// 1. Install node on your machine.
// 2. Install the _Grunt-CLI_ as Root: `sudo npm install -g grunt-cli`
// 3. Issue `npm install` in project directory to get Grunt and it's dependencies
// 4. Issue `grunt` in project directory to build an uglified library.
//
// ### Issues
// Visit [our Atlassian Jira](https://jira.fork.de/browse/ZEN) instance to post issues.
//
// ### License
// Copyright (C) 2013 Fork Unstable Media GmbH
//
// Zen-Shaw is licensed within the terms of the MIT license.

var Zen = (function($) {
	'use strict';

	var dataSource,
		parser = emmet.require('abbreviationParser');;

	function each(array, fn) {
		for (var i = 0, len = array.length; i < len; i++) {
			if (fn.call(array[i], i) === false) return;
		}
	}
	function includes(haystack, needle) {
		var isIncluded = false;
		each(haystack, function () {
			return !(isIncluded = this.name === needle);
		});
		return isIncluded;
	}

	// ## Our extra portion of syntax
	// This function does the magic of implicit tag names
	function getName(properties) {
		if (properties._name) return properties._name;

		switch (properties.parent._name) {
		// example: `ul > {List item #$}*7`
    // =>

    //
List item #1

    //
List item #2

    //
List item #3

    //
List item #4

    //
List item #5

    //
List item #6

    //
List item #7

    //

		case 'ol':
		case 'ul':
			return 'li';
		// example: `table[border=1] > .row*5 > {Test}*3`
    // =>
    //

    //

    //        Test
    //        Test
    //        Test
    //

    //

    //        Test
    //        Test
    //        Test
    //

    //

    //        Test
    //        Test
    //        Test
    //

    //

    //        Test
    //        Test
    //        Test
    //

    //

    //        Test
    //        Test
    //        Test
    //

    //

		case 'table':
			return 'tr';
		case 'tr':
			return 'td';
		// example: `select > {Option#$}*5`
    // => <select>
    //      <option>Option#1</option>
    //      <option>Option#2</option>
    //      <option>Option#3</option>
    //      <option>Option#4</option>
    //      <option>Option#5</option>
    //    </select>
		case 'select':
			return 'option';
		// example: `nav > #test-ul > {Test1} + {Test2}`
    // =>
    //

    //
Test1

    //
Test2

    //

    //
		case 'nav':
			return 'ul';
		// childs of `form` are `input`-tags, if they have a `type`-attribute. example: `form > #username[type=text] + #password[type=password]`
    // => <form>
    //      <input type ="text>username">username

    //      <input type="password">password
    //    </form>
		case 'form':
			var isInput = includes(properties._attributes, 'type');
			return isInput ? 'input' : 'div';
		// childs of `head` are `meta`-tags, if they have a `content`-attribute. example: `head > [content="UTF-8"] + [name=viewport content="width=device-width"]`
    // =>
    //    ```<head>
    //      <meta content="UTF-8">
    //      <meta name="viewport" content="width=decice-width">
    //    </head>```

		case 'head':
			var isMeta = includes(properties._attributes, 'content');
			return isMeta ? 'meta' : 'div';
		default:
			return 'div';
		}
	}

	function interpolate(value, counter) {
		var result = value;
		var key = result.match(/\$:([a-z]+)/i);
		var replacement = dataSource(key[1], counter - 1);
		if(typeof replacement !== 'undefined') {
			return result.replace(key[0], replacement);
		} else {
			return result.replace(key[0], "[[specify value for "+key[0]+"]]");
		}
	}

	function checkMarker(textRaw, position) {
		if(textRaw.charAt(position-1) == "\\") return "escaped";
		if(textRaw.charAt(position+1) == ":") return "token";
		return "counter";
	}

	// ## Additional content functions
	// By default, Emmet only supports plain text within curly braces
	function buildNodeText(textRaw, counter) {
		var occurrences = getIndices(textRaw),
			searchText = "",
			text = textRaw.substring(0, occurrences[0]),
			startPos = occurrences[0],
			endPos = (occurrences.length>1) ? occurrences[1] : textRaw.length,
			mode;

		for(var i=0;i<occurrences.length;i++) {

			searchText = textRaw.substring(startPos,endPos);
			mode = checkMarker(textRaw, occurrences[i]);

			// Escaping Emmet keys like `$`. Example: `span{You have to save \\$1000}`=> You have to save $1000
			if(mode ==  'escaped') {
				text+=searchText;
				text=text.replace(/\\/,"");
			}
			// This parses the inputDataSource as explained at the Main-Function lower on this page
			if(mode ==  'token') {
				text+=interpolate(searchText, counter);
			}
			// [The good, old counter](http://docs.emmet.io/abbreviations/syntax/#item-numbering-) is now also working to set the elements content
			if(mode ==  'counter') {
				searchText=searchText.replace("$",counter);
				text+=searchText;
			}

			startPos = endPos;
			endPos = (occurrences.length>=i+3) ? occurrences[i+2] : textRaw.length;
		}

		return text;
	}

	function getIndices(value) {
	    var startIndex = 0;
	    var index, indices = [];
	    while ((index = value.indexOf("$", startIndex)) > -1) {
	        indices.push(index);
	        startIndex = index + 1;
	    }
	    return indices;
	}

	function buildNode(properties) {
		var name = getName(properties);
		var node = document.createElement(name);

		if (properties._text) {
			// `textContent` is not supported by IE8. [Use a shim](https://forrst.com/posts/textContent_in_IE8-eNC).
			if(properties._text.indexOf('$') < 0) {
				node.textContent = properties._text;
			} else {
				node.textContent = buildNodeText(properties._text, properties.counter);
			}
		}

		each(properties.children, function () {
			this.parent._name = name;
		});

		each(properties._attributes, function () {
			this.value = buildNodeText(this.value, properties.counter);
			node.setAttribute(this.name, this.value);
		});

		return node;
	}

	function traverseTree(base, context){
		var node = buildNode(base);
		context.appendChild(node);
		each(base.children, function() {
			traverseTree(this, node);
		});
	}

	function getterFor(data) {
		return function(key, index) {
			var value = data[key];

			if (typeof value === 'object') {
				if (value.length > index) {
					return value[index];
				} else {
					return value[index-value.length]
				}
			}

			if (typeof value === 'function') {
				return value.call(this, index);
			}

			return value;
		};
	}

	// ## The main function
	// returns `documentFragment`
	function Zen(expression, inputDataSource) {
		// The special thing about **Zen-Shaw**: Submitting data that you can use to build your DOM.
		// For example:
		//
		// ```javascript
		// Zen('ul > #test-$:b{$:i}*3', {
		// 	'b': [1, 2, 3],
		// 	'i': ['foo', 'bar', 'baz']
		// });
		// ```
		//
		// It's also possible to submit a function as inputDataSource:
		//
		// ```javascript
		// var classNames = ["foo", "bar", "baz"];
		// Zen('ul > {$:i}*3', function (key, index) {
		// 	return (key === 'i') ? classNames[index] : 'xxx';
		// });
		// ```
		//
		if(typeof(inputDataSource) == 'undefined') {
			dataSource = function() { return false }
		}
		else if (typeof(inputDataSource) !== 'function') {
			dataSource = getterFor(inputDataSource);
		} else {
			dataSource = inputDataSource;
		}

		var fragment = document.createDocumentFragment();
		var nodes = parser.parse(expression).children;
		each(nodes, function () {
			traverseTree(this, fragment);
		});
		return fragment;
	};

	// ## jQuery integration
	// The functions take the same arguments as `Zen()`
	if ($) {
		// Okay, `Zen()` is now also reachable by `$.zen()`
		$.zen = function(expression, dataSource) {
			var fragment = Zen(expression, dataSource);
			return $(fragment);
		}
		// Set the inner HTML of the given object. Example: `$('body').zen('table[border=1]>.row*5>{Test}*3');`
		$.fn.zen = function(expression, dataSource) {
			var fragment = Zen(expression, dataSource);
			return this.each(function() {
				var clone = fragment.cloneNode(true);
				this.innerHTML = '';
				this.appendChild(clone);
			});
		};
	}

	return Zen;

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


<commit_before>module.exports = function(core, resource) {

  this.all = function(query, callback) {
    return core
      .get(resource)
      .query(query)
      .end(callback);
  }

  this.one = function(id, callback) {
    return core
      .get(resource + '/' + id)
      .end(callback);
  }

  this.create = function(body, callback) {
    return core
      .post(resource, body)
      .end(callback);
  }

  this.update = function(id, body, callback) {
    return core
      .put(resource + '/' + id, body)
      .end(callback);
  }

  this.destroy = function(id, callback) {
    return core
      .delete(resource + '/' + id)
      .end(callback);
  }
}<commit_msg>Fixed that query thing brian mentoined<commit_after>module.exports = function(core, resource) {

  this.all = function(query, callback) {
    if(arguments.length === 1) {
      callback = query;
      query = {};
    }
    return core
      .get(resource)
      .query(query)
      .end(callback);
  }

  this.one = function(id, query, callback) {
    if(arguments.length === 2) {
      callback = query;
      query = {};
    }

    return core
      .get(resource + '/' + id)
      .query(query)
      .end(callback);
  }

  this.create = function(body, callback) {
    return core
      .post(resource, body)
      .end(callback);
  }

  this.update = function(id, body, callback) {
    return core
      .put(resource + '/' + id, body)
      .end(callback);
  }

  this.destroy = function(id, callback) {
    return core
      .delete(resource + '/' + id)
      .end(callback);
  }
}
<|endoftext|>


<commit_before>let debug = require('debug')('taskcluster-github');
let crypto = require('crypto');
let API = require('taskcluster-lib-api');
let _ = require('lodash');

// Common schema prefix
let SCHEMA_PREFIX_CONST = 'http://schemas.taskcluster.net/github/v1/';

// Strips/replaces undesirable characters which GitHub allows in
// repository/organization names (notably .)
function sanitizeGitHubField(field) {
  return field.replace(/[^a-zA-Z0-9-_\.]/gi, '').replace(/\./g, '%');
};

// Reduce a pull request WebHook's data to only fields needed to checkout a
// revision
function getPullRequestDetails(eventData) {
  return {
    'event.type': 'pull_request.' + eventData.action,
    'event.base.repo.branch': eventData.pull_request.base.label.split(':')[1],
    'event.pullNumber': eventData.number,
    'event.base.user.login': eventData.pull_request.base.user.login,
    'event.base.repo.url': eventData.pull_request.base.repo.clone_url,
    'event.base.sha': eventData.pull_request.base.sha,
    'event.base.ref': eventData.pull_request.base.ref,
    'event.head.user.login': eventData.pull_request.head.user.login,
    'event.head.repo.url': eventData.pull_request.head.repo.clone_url,
    'event.head.repo.branch': eventData.pull_request.head.label.split(':')[1],
    'event.head.sha': eventData.pull_request.head.sha,
    'event.head.ref': eventData.pull_request.head.ref,
  };
};

function getPushDetails(eventData) {
  let ref = eventData.ref;
  // parsing the ref refs/heads/<branch-name> is the most reliable way
  // to get a branch name
  let branch = ref.split('/')[2];
  return {
    'event.type': 'push',
    'event.base.repo.branch': branch,
    'event.head.repo.branch': branch,
    'event.head.user.login': eventData.sender.login,
    'event.head.repo.url': eventData.repository.clone_url,
    'event.head.sha': eventData.after,
    'event.head.ref': ref,
    'event.base.sha': eventData.before,
  };
};

function getReleaseDetails(eventData) {
  return {
    'event.type': 'release',
    'event.base.repo.branch': eventData.release.target_commitish,
    'event.head.user.login': eventData.release.author.login,
    'event.version': eventData.release.tag_name,
    'event.name': eventData.release.name,
    'event.head.repo.url': eventData.release.url,
    'event.prerelease': eventData.release.prerelease,
    'event.draft': eventData.release.draft,
    'event.tar': eventData.release.tarball_url,
    'event.zip': eventData.release.zipball_url,
  };
}

/**
 * Hashes a payload by some secret, using the same algorithm that
 * GitHub uses to compute their X-Hub-Signature HTTP header. Used
 * for verifying the legitimacy of WebHooks.
 **/
function generateXHubSignature(secret, payload) {
  return 'sha1=' + crypto.createHmac('sha1', secret).update(payload).digest('hex');
};

/**
 * Compare hmac.digest('hex') signatures in constant time
 * Double hmac verification is the preferred way to do this
 * since we can't predict optimizations performed by the runtime.
 * https: *www.isecpartners.com/blog/2011/february/double-hmac-verification.aspx
 **/
function compareSignatures(sOne, sTwo) {
  let secret = Math.random().toString();
  let h1 = crypto.createHmac('sha1', secret).update(sOne);
  let h2 = crypto.createHmac('sha1', secret).update(sTwo);
  return h1.digest('hex') === h2.digest('hex');
};

function resolve(res, status, message) {
  debug(message);
  return res.status(status).send(message);
}

/** API end-point for version v1/
 *
 * In this API implementation we shall assume the following context:
 * {
 *   publisher:      // publisher from base.Exchanges
 * }
 */
let api = new API({
  title:        'TaskCluster GitHub API Documentation',
  description: [
    'The github service, typically available at',
    '`github.taskcluster.net`, is responsible for publishing pulse',
    'messages in response to GitHub events.',
    '',
    'This document describes the API end-point for consuming GitHub',
    'web hooks',
  ].join('\n'),
  schemaPrefix: 'http://schemas.taskcluster.net/github/v1/',
  context: ['Builds', 'monitor'],
});

// Export API
module.exports = api;

/** Define tasks */
api.declare({
  method:     'post',
  route:      '/github',
  name:       'githubWebHookConsumer',
  title:      'Consume GitHub WebHook',
  stability:  'experimental',
  description: [
    'Capture a GitHub event and publish it via pulse, if it\'s a push,',
    'release or pull request.',
  ].join('\n'),
}, async function(req, res) {
  let eventType = req.headers['x-github-event'];
  if (!eventType) {
    return resolve(res, 400, 'Missing X-GitHub-Event');
  }

  let body = req.body;
  if (!body) {
    return resolve(res, 400, 'Request missing a body');
  }

  let webhookSecrets = this.cfg.webhook.secret;
  let xHubSignature = req.headers['x-hub-signature'];

  if (xHubSignature && !webhookSecrets) {
    return resolve(res, 400, 'Server is not setup to handle secrets');
  } else if (webhookSecrets && !xHubSignature) {
    return resolve(res, 400, 'Request missing a secret');
  } else if (webhookSecrets && xHubSignature) {
    // Verify that our payload is legitimate
    if (!webhookSecrets.some(webhookSecret => {
      let calculatedSignature = generateXHubSignature(webhookSecret,
        JSON.stringify(body));
      return compareSignatures(calculatedSignature, xHubSignature);
    })) {
      return resolve(res, 403, 'X-hub-signature does not match; bad webhook secret?');
    }
  }

  let msg = {};
  let publisherKey = '';

  try {
    if (eventType == 'pull_request') {
      msg.organization = sanitizeGitHubField(body.repository.owner.login),
      msg.action = body.action;
      msg.details = getPullRequestDetails(body);
      publisherKey = 'pullRequest';
    } else if (eventType == 'push') {
      msg.organization = sanitizeGitHubField(body.repository.owner.name),
      msg.details = getPushDetails(body);
      publisherKey = 'push';
    } else if (eventType == 'ping') {
      return resolve(res, 200, 'Received ping event!');
    } else if (eventType == 'release') {
      msg.organization = sanitizeGitHubField(body.repository.owner.login),
      msg.details = getReleaseDetails(body);
      publisherKey = 'release';
    } else {
      return resolve(res, 400, 'No publisher available for X-GitHub-Event: ' + eventType);
    }
  } catch (e) {
    e.webhookPayload = body;
    throw e;
  }

  // Not all webhook payloads include an e-mail for the user who triggered
  // an event.
  let headUser = msg.details['event.head.user.login'];
  let userDetails = await this.github.users.get(headUser);

  msg.details['event.head.user.email'] = userDetails.email || headUser + '@users.noreply.github.com';
  msg.repository = sanitizeGitHubField(body.repository.name);

  await this.publisher[publisherKey](msg);

  res.status(204).send();
});

api.declare({
  method:     'get',
  route:      '/builds',
  name:       'builds',
  title:      'List of Builds',
  stability:  'experimental',
  output:     'build-list.json#',
  query: {
    continuationToken: /./,
    limit: /^[0-9]+$/,
    organization: /^([a-zA-Z0-9-_%]*)$/,
    repository: /^([a-zA-Z0-9-_%]*)$/,
    sha: /./,
  },
  description: [
    'A paginated list of builds that have been run in',
    'Taskcluster. Can be filtered on various git-specific',
    'fields.',
  ].join('\n'),
}, async function(req, res) {
  let continuation = req.query.continuationToken || null;
  let limit = parseInt(req.query.limit || 1000, 10);
  let query = _.pick(req.query, ['organization', 'repository', 'sha']);
  let builds = await this.Builds.scan(query, {continuation, limit});
  return res.reply({
    continuationToken: builds.continuation || '',
    builds: builds.entries.map(entry => {
      return {
        organization: entry.organization,
        repository: entry.repository,
        sha: entry.sha,
        state: entry.state,
        taskGroupId: entry.taskGroupId,
        created: entry.created.toJSON(),
        updated: entry.updated.toJSON(),
      };
    }),
  });
});
<commit_msg>Corrected clone url<commit_after>let debug = require('debug')('taskcluster-github');
let crypto = require('crypto');
let API = require('taskcluster-lib-api');
let _ = require('lodash');

// Common schema prefix
let SCHEMA_PREFIX_CONST = 'http://schemas.taskcluster.net/github/v1/';

// Strips/replaces undesirable characters which GitHub allows in
// repository/organization names (notably .)
function sanitizeGitHubField(field) {
  return field.replace(/[^a-zA-Z0-9-_\.]/gi, '').replace(/\./g, '%');
};

// Reduce a pull request WebHook's data to only fields needed to checkout a
// revision
function getPullRequestDetails(eventData) {
  return {
    'event.type': 'pull_request.' + eventData.action,
    'event.base.repo.branch': eventData.pull_request.base.label.split(':')[1],
    'event.pullNumber': eventData.number,
    'event.base.user.login': eventData.pull_request.base.user.login,
    'event.base.repo.url': eventData.pull_request.base.repo.clone_url,
    'event.base.sha': eventData.pull_request.base.sha,
    'event.base.ref': eventData.pull_request.base.ref,
    'event.head.user.login': eventData.pull_request.head.user.login,
    'event.head.repo.url': eventData.pull_request.head.repo.clone_url,
    'event.head.repo.branch': eventData.pull_request.head.label.split(':')[1],
    'event.head.sha': eventData.pull_request.head.sha,
    'event.head.ref': eventData.pull_request.head.ref,
  };
};

function getPushDetails(eventData) {
  let ref = eventData.ref;
  // parsing the ref refs/heads/<branch-name> is the most reliable way
  // to get a branch name
  let branch = ref.split('/')[2];
  return {
    'event.type': 'push',
    'event.base.repo.branch': branch,
    'event.head.repo.branch': branch,
    'event.head.user.login': eventData.sender.login,
    'event.head.repo.url': eventData.repository.clone_url,
    'event.head.sha': eventData.after,
    'event.head.ref': ref,
    'event.base.sha': eventData.before,
  };
};

function getReleaseDetails(eventData) {
  return {
    'event.type': 'release',
    'event.base.repo.branch': eventData.release.target_commitish,
    'event.head.user.login': eventData.release.author.login,
    'event.version': eventData.release.tag_name,
    'event.name': eventData.release.name,
    'event.head.repo.url': eventData.repository.clone_url,
    'event.release.url': eventData.release.url,
    'event.prerelease': eventData.release.prerelease,
    'event.draft': eventData.release.draft,
    'event.tar': eventData.release.tarball_url,
    'event.zip': eventData.release.zipball_url,
  };
}

/**
 * Hashes a payload by some secret, using the same algorithm that
 * GitHub uses to compute their X-Hub-Signature HTTP header. Used
 * for verifying the legitimacy of WebHooks.
 **/
function generateXHubSignature(secret, payload) {
  return 'sha1=' + crypto.createHmac('sha1', secret).update(payload).digest('hex');
};

/**
 * Compare hmac.digest('hex') signatures in constant time
 * Double hmac verification is the preferred way to do this
 * since we can't predict optimizations performed by the runtime.
 * https: *www.isecpartners.com/blog/2011/february/double-hmac-verification.aspx
 **/
function compareSignatures(sOne, sTwo) {
  let secret = Math.random().toString();
  let h1 = crypto.createHmac('sha1', secret).update(sOne);
  let h2 = crypto.createHmac('sha1', secret).update(sTwo);
  return h1.digest('hex') === h2.digest('hex');
};

function resolve(res, status, message) {
  debug(message);
  return res.status(status).send(message);
}

/** API end-point for version v1/
 *
 * In this API implementation we shall assume the following context:
 * {
 *   publisher:      // publisher from base.Exchanges
 * }
 */
let api = new API({
  title:        'TaskCluster GitHub API Documentation',
  description: [
    'The github service, typically available at',
    '`github.taskcluster.net`, is responsible for publishing pulse',
    'messages in response to GitHub events.',
    '',
    'This document describes the API end-point for consuming GitHub',
    'web hooks',
  ].join('\n'),
  schemaPrefix: 'http://schemas.taskcluster.net/github/v1/',
  context: ['Builds', 'monitor'],
});

// Export API
module.exports = api;

/** Define tasks */
api.declare({
  method:     'post',
  route:      '/github',
  name:       'githubWebHookConsumer',
  title:      'Consume GitHub WebHook',
  stability:  'experimental',
  description: [
    'Capture a GitHub event and publish it via pulse, if it\'s a push,',
    'release or pull request.',
  ].join('\n'),
}, async function(req, res) {
  let eventType = req.headers['x-github-event'];
  if (!eventType) {
    return resolve(res, 400, 'Missing X-GitHub-Event');
  }

  let body = req.body;
  if (!body) {
    return resolve(res, 400, 'Request missing a body');
  }

  let webhookSecrets = this.cfg.webhook.secret;
  let xHubSignature = req.headers['x-hub-signature'];

  if (xHubSignature && !webhookSecrets) {
    return resolve(res, 400, 'Server is not setup to handle secrets');
  } else if (webhookSecrets && !xHubSignature) {
    return resolve(res, 400, 'Request missing a secret');
  } else if (webhookSecrets && xHubSignature) {
    // Verify that our payload is legitimate
    if (!webhookSecrets.some(webhookSecret => {
      let calculatedSignature = generateXHubSignature(webhookSecret,
        JSON.stringify(body));
      return compareSignatures(calculatedSignature, xHubSignature);
    })) {
      return resolve(res, 403, 'X-hub-signature does not match; bad webhook secret?');
    }
  }

  let msg = {};
  let publisherKey = '';

  try {
    if (eventType == 'pull_request') {
      msg.organization = sanitizeGitHubField(body.repository.owner.login),
      msg.action = body.action;
      msg.details = getPullRequestDetails(body);
      publisherKey = 'pullRequest';
    } else if (eventType == 'push') {
      msg.organization = sanitizeGitHubField(body.repository.owner.name),
      msg.details = getPushDetails(body);
      publisherKey = 'push';
    } else if (eventType == 'ping') {
      return resolve(res, 200, 'Received ping event!');
    } else if (eventType == 'release') {
      msg.organization = sanitizeGitHubField(body.repository.owner.login),
      msg.details = getReleaseDetails(body);
      publisherKey = 'release';
    } else {
      return resolve(res, 400, 'No publisher available for X-GitHub-Event: ' + eventType);
    }
  } catch (e) {
    e.webhookPayload = body;
    throw e;
  }

  // Not all webhook payloads include an e-mail for the user who triggered
  // an event.
  let headUser = msg.details['event.head.user.login'];
  let userDetails = await this.github.users.get(headUser);

  msg.details['event.head.user.email'] = userDetails.email || headUser + '@users.noreply.github.com';
  msg.repository = sanitizeGitHubField(body.repository.name);

  await this.publisher[publisherKey](msg);

  res.status(204).send();
});

api.declare({
  method:     'get',
  route:      '/builds',
  name:       'builds',
  title:      'List of Builds',
  stability:  'experimental',
  output:     'build-list.json#',
  query: {
    continuationToken: /./,
    limit: /^[0-9]+$/,
    organization: /^([a-zA-Z0-9-_%]*)$/,
    repository: /^([a-zA-Z0-9-_%]*)$/,
    sha: /./,
  },
  description: [
    'A paginated list of builds that have been run in',
    'Taskcluster. Can be filtered on various git-specific',
    'fields.',
  ].join('\n'),
}, async function(req, res) {
  let continuation = req.query.continuationToken || null;
  let limit = parseInt(req.query.limit || 1000, 10);
  let query = _.pick(req.query, ['organization', 'repository', 'sha']);
  let builds = await this.Builds.scan(query, {continuation, limit});
  return res.reply({
    continuationToken: builds.continuation || '',
    builds: builds.entries.map(entry => {
      return {
        organization: entry.organization,
        repository: entry.repository,
        sha: entry.sha,
        state: entry.state,
        taskGroupId: entry.taskGroupId,
        created: entry.created.toJSON(),
        updated: entry.updated.toJSON(),
      };
    }),
  });
});
<|endoftext|>


<commit_before>module.exports = {
  v1: require('./endpoints/v1'),
  models: {
    Account: require('./models/account'),
    Award: require('./models/award'),
    Base: require('./models/base'),
    Comment: require('./models/comment'),
    Link: require('./models/link'),
    Message: require('./models/message'),
    Promocampaign: require('./models/promocampaign'),
    Subreddit: require('./models/subreddit'),
    Vote: require('./models/Vote'),
  },
}
<commit_msg>Capitalization how does it work. Vote -> vote<commit_after>module.exports = {
  v1: require('./endpoints/v1'),
  models: {
    Account: require('./models/account'),
    Award: require('./models/award'),
    Base: require('./models/base'),
    Comment: require('./models/comment'),
    Link: require('./models/link'),
    Message: require('./models/message'),
    Promocampaign: require('./models/promocampaign'),
    Subreddit: require('./models/subreddit'),
    Vote: require('./models/vote'),
  },
}
<|endoftext|>


<commit_before>var chalk = require('chalk');
var glob = require('glob');
var Checker = require('jscs');
var exit = require('exit');
var fs = require('fs');
var glob = require('glob');
var extend = require('extend');
var logSymbols = require('log-symbols');

var data, parsedCode, checker, parser;

module.exports = function (config) {
  extend(config, {
    files: config.files || [],
    write: config.write || false,
    jscs: config.jscs,
    parser: config.parser || 'ast',
    preset: config.preset || 'jquery'
  });

  if (typeof config.jscs === 'undefined') {
    config.jscs = true;
  }

  try {
    parser = require('./' + config.parser);
  } catch (e) {
    console.log(logSymbols.error + ' ' +
      chalk.bold.red('Failed to load parser: %s'), config.parser);
    console.log(logSymbols.info,
      chalk.bold.yellow('Info: Parser should be either ast/regex'));
    console.log(e.toString());
    exit();
  }

  if (config.files.length === 0) {
    console.log(logSymbols.error, chalk.bold.red('No files passed!'));
    console.log(logSymbols.info, chalk.bold.yellow('Use -h for help'));
    exit();
  }

  if (config.jscs) {
    checker = new Checker();
    var jscsConfig = config.jscsConfig || {};
    extend(jscsConfig, {
      preset: config.preset,
      fix: true
    });

    checker.registerDefaultRules();
    checker.configure(jscsConfig);
  }

  if (Array.isArray(config.files)) {
    parseFiles(null, config.files);
  } else {
    glob(config.files, parseFiles);
  }

  function parseFiles(error, files) {
    if (error) {
      console.log(logSymbols.error, chalk.bold.red('Error in Globbing pattern!'));
      console.log(error.toString());
      exit();
    }

    var errors = [], passed = [];

    files.forEach(function (file) {
      try {
        data = fs.readFileSync(file);
        parsedCode = parser(data);

        if (config.jscs) {
          parsedCode = checker.fixString(parsedCode);
          parsedCode = parsedCode.output;
        }

        if (config.write) {
          fs.writeFileSync(file, parsedCode);
        } else {
          console.log(logSymbols.success +
            ' ' + chalk.bold.green('Output for %s:\n'), file);
          console.log(parsedCode);
        }

        passed.push(file);
      } catch (e) {
        console.log(logSymbols.error + ' ' +
          chalk.bold.red('Error occured while fixing file: %s'), file);

        console.log(e.toString());
        errors.push(file);
      }
    });

    if (errors.length) {
      console.log(chalk.bold.red('Following files were not converted:'));
      errors.forEach(function (error) {
        console.log(logSymbols.error, chalk.red(error));
      });
    }

    if (passed.length) {
      console.log(chalk.bold.green('Following files were successfully converted:'));
      passed.forEach(function (pass) {
        console.log(logSymbols.success, chalk.green(pass));
      });
    }
  }
};
<commit_msg>Add api src for codebases<commit_after>var ast = require('./ast');
var regex = require('./regex');

module.exports = {
  ast: ast,
  regex: regex
};
<|endoftext|>


<commit_before>
// Default db
// - "/schema/errors"
// dynamic node mounded so that actions or other
// nodes can listen to when the data is incompatible with the schema
// and has errors
//
// also instead of adding another method to the
// exposed api the developer can use db.get("/schema/errors")
// when it needs it

// triggerPath(path)
// ------
// Triggers an update at the given path
// informing all listeners of the new values
//
// 1. Unless at root, generated all paths until root
// 2. Match all listenersTriggers to get the listeners
// 3. For each listener get its nodeValue
// 4. Call each fn with the value

// nodeValue(path)
// ------
// Get the value at the given path regardless if its
// a dynamic or static node
//
// 1. if is a dynamic node then get nodeValue for each of its node
// 2. call the fn with the value args
// 3. return the result
//
// 4. else clone the static node
// 5  get all dynamic nodes beneath that path (using dynamicNodesLocation) and get their nodeValue
// 6. append the results to the cloned object
// 7. return the result

// findStatic(path)
// ------
// Find all the static paths that when changed will influence
// the given path
//
// 1. if path is a static node then return [path]
// 2. else findStatic on the nodes of the dynamic nodes
// 3. flatten the result and return

// unsubscribe(path)
// ------
// Removes a listener from the given path
// Once called it needs to make sure synchronusely
// that no other process will call the listener until finished
//
// 1. remove it from listeners
// 2. remote it from the 'listenersTriggers'

// addListener(path, fn)
// ------
// Creates a listener on the given path
// which will call the fn with the updated value
//
// 1. create a listener at the path in "listeners"
// 2. findStatic at the given path and add them to "listenersTriggers"
// 3. return unsubscribe(path) function

// checkCyclic(parent, children)
// ------
// Checks if at the new mounted path there will be a cyclic
// relation with it's children

// addDynamicNode(path, paths, fn)
// ------
// Create a new dynamic node
//
// 1. checkCyclic tree at the given path with the nodes
// 2. add an entry in "dynamicNodes"
// 2. generate all the paths from the path to the root and add them to "dynamicNodesLocations"

// applyPatch(patches)
// ------
// Apply one or more patches
//
// 1. if patches if not an array make it one
// 2. verify each patch for errors before applying (allow data format errors but not schema errors)
// 3. apply patches to db
// 4. triggerPath on each affected path

// pathExists(path)
// -----
// Checks if a given path exists

// -----
// Exposed api
// -----
// on: addListener
// get: nodeValue
// has: pathExists
// patch: applyPatch
<commit_msg>Finish API overview<commit_after>
// Default db
// - "/schema/errors"
// dynamic node mounded so that actions or other
// nodes can listen to when the data is incompatible with the schema
// and has errors
//
// also instead of adding another method to the
// exposed api the developer can use db.get("/schema/errors")
// when it needs it

// -------
// Ideas
// -------
// A schema can be added only once - after that the system
// does not allow any other schema to be added.
// In this way the db can be used without a schema (not recommened)
// but also provide a runtime mode that does not go through
// a build process - which makes it easier for developers
// to use and try out the system.

// triggerPath(path)
// ------
// Triggers an update at the given path
// informing all listeners of the new values
//
// 1. Unless at root, generated all paths until root
// 2. Match all listenersTriggers to get the listeners
// 3. For each listener get its nodeValue
// 4. Call each fn with the value

// nodeValue(path)
// ------
// Get the value at the given path regardless if its
// a dynamic or static node
//
// 1. if is a dynamic node then get nodeValue for each of its node
// 2. call the fn with the value args
// 3. return the result
//
// 4. else clone the static node
// 5  get all dynamic nodes beneath that path (using dynamicNodesLocation) and get their nodeValue
// 6. append the results to the cloned object
// 7. return the result

// findStatic(path)
// ------
// Find all the static paths that when changed will influence
// the given path
//
// 1. if path is a static node then return [path]
// 2. else findStatic on the nodes of the dynamic nodes
// 3. flatten the result and return

// unsubscribe(path)
// ------
// Removes a listener from the given path
// Once called it needs to make sure synchronusely
// that no other process will call the listener until finished
//
// 1. remove it from listeners
// 2. remote it from the 'listenersTriggers'

// addListener(path, fn)
// ------
// Creates a listener on the given path
// which will call the fn with the updated value
//
// 1. create a listener at the path in "listeners"
// 2. findStatic at the given path and add them to "listenersTriggers"
// 3. return unsubscribe(path) function

// checkCyclic(parent, children)
// ------
// Checks if at the new mounted path there will be a cyclic
// relation with it's children

// addDynamicNode(path, paths, fn)
// ------
// Create a new dynamic node
//
// 1. checkCyclic tree at the given path with the nodes
// 2. add an entry in "dynamicNodes"
// 2. generate all the paths from the path to the root and add them to "dynamicNodesLocations"

// applyPatch(patches)
// ------
// Apply one or more patches
//
// 1. if patches if not an array make it one
// 2. verify each patch for errors before applying (allow data format errors but not schema errors)
// 3. apply patches to db
// 4. triggerPath on each affected path

// pathExists(path)
// -----
// Checks if a given path exists

// useSchema(schema)
// ----
// Makes this the schema to be used.
// Cannot be changed later.

// -----
// Exposed api
// -----
// on: addListener
// get: nodeValue
// has: pathExists
// patch: applyPatch
// useSchema: useSchema
<|endoftext|>


<commit_before>// @flow
import * as Types from '../../constants/types/fs'
import * as Constants from '../../constants/fs'
import * as FsGen from '../fs-gen'
import type {TypedActions} from '../typed-actions-gen'

const makeErrorHandler = (action: FsGen.Actions, path: ?Types.Path, retriable: boolean) => (
  error: any
): Array<TypedActions> => {
  if (path) {
    // TODO: KBFS-4143 add and use proper error code for all these
    if (typeof error.desc === 'string') {
      if (
        error.desc.includes('does not have read access to') ||
        error.desc.includes('You are not a member of this team') ||
        // team doesn't exist
        error.desc.includes('Root team does not exist') ||
        // public tlf doesn't exist
        error.desc.includes("Can't create TLF ID for non-team-backed handle") ||
        // /keybase/private/non_existent_user
        error.desc.includes(' is not a Keybase user')
      ) {
        const tlfPath = Constants.getTlfPath(path)
        if (tlfPath) {
          return [FsGen.createSetTlfSoftError({path: tlfPath, softError: 'no-access'})]
        }
      }
      if (error.desc.includes('file does not exist')) {
        return [FsGen.createSetPathSoftError({path, softError: 'non-existent'})]
      }
    }
    if (error.desc.includes('KBFS client not found.')) {
      return [
        FsGen.createKbfsDaemonRpcStatusChanged({rpcStatus: 'wait-timeout'}),
        // We don't retry actions when re-connected, so just route user back
        // to root in case they get confused by orphan loading state.
        //
        // Although this seems impossible to do for nav2 as things are just
        // pushed on top of each other, so just don't do anything for now.
        // Perhaps it's OK.
      ]
    }
  }
  return [
    FsGen.createFsError({
      error: Constants.makeError({
        error,
        erroredAction: action,
        retriableAction: retriable ? action : undefined,
      }),
    }),
  ]
}

export const makeRetriableErrorHandler = (action: FsGen.Actions, path: ?Types.Path) =>
  makeErrorHandler(action, path, true)

export const makeUnretriableErrorHandler = (action: FsGen.Actions, path: ?Types.Path) =>
  makeErrorHandler(action, path, false)
<commit_msg>fs/shared: fix error parse bug<commit_after>// @flow
import * as Types from '../../constants/types/fs'
import * as Constants from '../../constants/fs'
import * as FsGen from '../fs-gen'
import type {TypedActions} from '../typed-actions-gen'

const makeErrorHandler = (action: FsGen.Actions, path: ?Types.Path, retriable: boolean) => (
  error: any
): Array<TypedActions> => {
  if (path) {
    // TODO: KBFS-4143 add and use proper error code for all these
    if (typeof error.desc === 'string') {
      if (
        error.desc.includes('does not have read access to') ||
        error.desc.includes('You are not a member of this team') ||
        // team doesn't exist
        error.desc.includes('Root team does not exist') ||
        // public tlf doesn't exist
        error.desc.includes("Can't create TLF ID for non-team-backed handle") ||
        // /keybase/private/non_existent_user
        error.desc.includes(' is not a Keybase user')
      ) {
        const tlfPath = Constants.getTlfPath(path)
        if (tlfPath) {
          return [FsGen.createSetTlfSoftError({path: tlfPath, softError: 'no-access'})]
        }
      }
      if (error.desc.includes('file does not exist')) {
        return [FsGen.createSetPathSoftError({path, softError: 'non-existent'})]
      }
      if (error.desc.includes('KBFS client not found.')) {
        return [
          FsGen.createKbfsDaemonRpcStatusChanged({rpcStatus: 'wait-timeout'}),
          // We don't retry actions when re-connected, so just route user back
          // to root in case they get confused by orphan loading state.
          //
          // Although this seems impossible to do for nav2 as things are just
          // pushed on top of each other, so just don't do anything for now.
          // Perhaps it's OK.
        ]
      }
    }
  }
  return [
    FsGen.createFsError({
      error: Constants.makeError({
        error,
        erroredAction: action,
        retriableAction: retriable ? action : undefined,
      }),
    }),
  ]
}

export const makeRetriableErrorHandler = (action: FsGen.Actions, path: ?Types.Path) =>
  makeErrorHandler(action, path, true)

export const makeUnretriableErrorHandler = (action: FsGen.Actions, path: ?Types.Path) =>
  makeErrorHandler(action, path, false)
<|endoftext|>