<commit_before>import '../env'
import url from 'url'
import Twit from 'twit'
import log from './logger'
import { timer } from './utils'
import TwitterV2 from 'twitter-v2'
import emojiRegex from 'emoji-regex'
import { AllHtmlEntities } from 'html-entities'
import { flatten, EVERYTHING } from 'flatten-tweet'

const emojiMatch = emojiRegex()
const entities = new AllHtmlEntities()

function decode(s) {
  return entities.decode(s)
}

export class Twitter {

  constructor(keys = {}) {
    this.consumerKey = keys.consumerKey || process.env.CONSUMER_KEY
    this.consumerSecret = keys.consumerSecret || process.env.CONSUMER_SECRET
    this.accessToken = keys.accessToken || process.env.ACCESS_TOKEN
    this.accessTokenSecret = keys.accessTokenSecret || process.env.ACCESS_TOKEN_SECRET

    // a client for v1.1 endpoints
    this.twit = new Twit({
      consumer_key: this.consumerKey,
      consumer_secret: this.consumerSecret,
      access_token: this.accessToken,
      access_token_secret: this.accessTokenSecret
    })

    // a client for v2 endpoints
    this.twitterV2 = new TwitterV2({
      consumer_key: this.consumerKey,
      consumer_secret: this.consumerSecret,
      access_token_key: this.accessToken,
      access_token_secret: this.accessTokenSecret
    })

    // a app auth client for v2 endpoints
    this.twitterV2app = new TwitterV2({
      consumer_key: this.consumerKey,
      consumer_secret: this.consumerSecret,
    })
  }

  getPlaces() {
    return new Promise((resolve) => {
      this.twit.get('trends/available').
        then((resp) => {
          const places = []
          for (const place of resp.data) {
            places.push({
              id: place.woeid,
              name: place.name,
              type: place.placeType.name,
              country: place.country || '',
              countryCode: place.countryCode || '',
              parentId: place.parentid || ''
            })
          }
          resolve(places)
        })
    })
  }

  async getTrendsAtPlace(id) {
    log.info('fetching trends for ' + id)
    const trends = []
    try {
      const resp = await this.twit.get('trends/place', {id: id})
      for (const trend of resp.data[0].trends) {
        trends.push({name: trend.name, count: trend.tweet_volume})
      }
    } catch (e) {
      log.error(`error when fetching trends: ${e}`)
    }
    return trends
  }

  search(opts, cb) {
    log.info('searching for', opts)
    // count is the total number of tweets to return across all API requests
    const count = opts.count || 100

    const params =  {
      ...EVERYTHING,
      "query": opts.q,
      "max_results": 100,
    }

    if (opts.sinceId) {
      params.since_id = opts.sinceId
    }

    if (opts.maxId) {
      params.until_id = opts.maxId
    }

    const endpoint = opts.all ? 'tweets/search/all' : 'tweets/search/recent'

    const recurse = (nextToken, total) => {
      if (nextToken) {
        params.next_token = nextToken
      }
      this.twitterV2.get(endpoint, params).then((resp) => {
        if (resp.data) {
          const tweets = flatten(resp).data
          const newTotal = total + tweets.length
          cb(null, tweets.map(t => this.extractTweet(t)))
            .then(() => {
              if (newTotal < count && resp.meta.next_token) {
                recurse(resp.meta.next_token, newTotal)
              } else {
                cb(null, [])
              }
            })
        } else {
          log.warn(`received search response with no data stanza`)
          cb(null, [])
        }
      })
      .catch(err => {
        log.error(`error during search: ${err}`)
        cb(err, null)
      })
    }

    recurse(null, 0)
  }

  async addFilterRule(value, tag) {
    log.info(`adding filter rule value=${value} tag=${tag}`)
    const payload = {"add": [{value, tag}]}
    const results = this.twitterV2app.post('tweets/search/stream/rules', payload)
    return results
  }

  async getFilterRules() {
    log.info('getting filter rules')
    const results = await this.twitterV2app.get('tweets/search/stream/rules')
    if (results.data) {
      return results.data
    } else {
      return []
    }
  }

  async deleteFilterRule(id) {
    log.info(`deleting filter rule ${id}`)
    const payload = {"delete": {"ids": [id]}}
    const results = this.twitterV2app.post('tweets/search/stream/rules', payload)
    return results
  }

  async filter(cb) {
    log.info('starting filter stream')

    let err = 0
    while (true) {
      try {
        this.stream = this.twitterV2app.stream('tweets/search/stream', EVERYTHING)
        break
      } catch (e) {
        err += 1
        const secs = err ** 2
        log.error(`caught ${e} while connecting to stream, sleeping ${secs}`)
        await timer(secs * 1000)
      }
    }

    try {
      for await (const response of this.stream) {
        if (response.data) {
          // when streaming response.data is an object, instead of a list
          // flatten ensures that response.data is always a list
          // so we want to get the first and only element in the list
          const tweet = flatten(response).data[0]
          const tags = response.matching_rules
            ? response.matching_rules.map(r => r.tag)
            : []

          // if the callback result is not true then we are being told to stop
          const result = await cb(this.extractTweet(tweet), tags)
          if (! result) {
            log.info('callback returned false so stopping filter stream')
            break
          }
        } else {
          log.error(`unexpected filter response: ${JSON.stringify(response)}`)
        }
      }
      // if stream is undefined that means closeFilter() has been called
      if (this.stream) {
        log.error(`stream disconnected normally by Twitter, reconnecting`)
        this.filter(cb)
      }
    } catch (error) {
      log.warn(`stream disconnected with error, retrying`, error)
      this.filter(cb)
    }
  }

  closeFilter() {
    if (this.stream) {
      log.info('closing filter stream')
      this.stream.close()
      this.stream = null
    }
  }

  lookup(o, includes) {
    return {
      ...o,
      ...includes.find(i => o.id === i.id)
    }
  }

  lookupList(list, includes) {
    return list ? list.map(o => this.lookup(o, includes)) : []
  }

  extractTweet(t) {
    let retweet = null
    let quote = null
    for (const ref of t.referenced_tweets || []) {
      if (ref.type == 'retweeted') {
        retweet = ref
      } else if (ref.type === 'quoted') {
        quote = ref
      }
    }

    const hashtags = t.entities && t.entities.hashtags
      ? t.entities.hashtags.map(ht => ht.tag.toLowerCase())
      : []

    const mentions = t.entities && t.entities.mentions
      ? t.entities.mentions.map(m => m.username)
      : []

    let place = null
    if (t.geo && t.geo.place_id) {
      place = {
        name: t.geo.full_name,
        type: t.geo.place_type,
        id: t.geo.place_id,
        country: t.geo.country,
        countryCode: t.geo.country_code,
        boundingBox: t.geo.geo.bbox
      }
    }

    const urls = []
    if (t.entities && t.entities.urls) {
      for (const e of t.entities.urls) {
        const u = url.parse(e.expanded_url)
        // not interested in pointers back to Twitter which
        // happens during quoting
        if (u.hostname === 'twitter.com') {
          continue
        }
        urls.push({
          short: e.url,
          long: e.expanded_url,
          hostname: u.hostname
        })
      }
    }

    const images = []
    const videos = []
    const animatedGifs = []
    if (t.attachments && t.attachments.media) {
      for (const e of t.attachments.media) {
        if (e.type === 'photo') {
          images.push(e.url)
        } else if (e.type === 'video') {
          videos.push(e.preview_image_url)
        } else if (e.type === 'animated_gif') {
          animatedGifs.push(e.preview_image_url)
        }
      }
    }

    return {
      id: t.id,
      created: new Date(t.created_at),
      twitterUrl: `https://twitter.com/${t.author.username}/status/${t.id}`,
      text: decode(t.text),
      language: t.lang,
      client: t.source,
      likeCount: t.public_metrics.like_count,
      retweetCount: t.public_metrics.retweet_count,
      quoteCount: t.public_metrics.quote_count,
      replyCount: t.public_metrics.quote_count,
      retweetId: retweet ? retweet.id : null,
      quoteId: quote ? quote.id : null,
      quote: quote,
      retweet: retweet,
      emojis: emojiMatch.exec(t.text),
      hashtags: hashtags,
      mentions: mentions,
      place: place,
      urls: urls,
      videos: videos,
      images: images,
      animatedGifs: animatedGifs,
      user: {
        id: t.author.id,
        created: new Date(t.author.created_at),
        screenName: t.author.username,
        name: decode(t.author.name),
        description: decode(t.author.description),
        location: decode(t.author.location),
        avatarUrl: t.author.profile_image_url,
        url: t.author.url,
        followersCount: t.author.public_metrics.followers_count,
        friendsCount: t.author.public_metrics.following_count,
        tweetsCount: t.author.public_metrics.tweet_count,
        listedCount: t.author.public_metrics.listed_count,
      },
    }

  }

  async sendTweet(text) {
    const result = await this.twit.post('statuses/update', {
      status: text
    })
    return result.data.id_str
  }

}<commit_msg>Don't create user Twitter clients if all keys are not available<commit_after>import '../env'
import url from 'url'
import Twit from 'twit'
import log from './logger'
import { timer } from './utils'
import TwitterV2 from 'twitter-v2'
import emojiRegex from 'emoji-regex'
import { AllHtmlEntities } from 'html-entities'
import { flatten, EVERYTHING } from 'flatten-tweet'

const emojiMatch = emojiRegex()
const entities = new AllHtmlEntities()

function decode(s) {
  return entities.decode(s)
}

export class Twitter {

  constructor(keys = {}) {
    this.consumerKey = keys.consumerKey || process.env.CONSUMER_KEY
    this.consumerSecret = keys.consumerSecret || process.env.CONSUMER_SECRET
    this.accessToken = keys.accessToken || process.env.ACCESS_TOKEN
    this.accessTokenSecret = keys.accessTokenSecret || process.env.ACCESS_TOKEN_SECRET

    // a client for v1.1 endpoints
    if (this.consumerKey && this.consumerSecret && this.accessToken && this.accessTokenSecret) {
      this.twit = new Twit({
        consumer_key: this.consumerKey,
        consumer_secret: this.consumerSecret,
        access_token: this.accessToken,
        access_token_secret: this.accessTokenSecret
      })

      // a client for v2 endpoints
      this.twitterV2 = new TwitterV2({
        consumer_key: this.consumerKey,
        consumer_secret: this.consumerSecret,
        access_token_key: this.accessToken,
        access_token_secret: this.accessTokenSecret
      })

    } else {
      log.warn('not configuring user client for v1.1 and v2 endpoints since not all keys are present')
    }

    // a app auth client for v2 endpoints
    if (this.consumerKey && this.consumerSecret) {
      this.twitterV2app = new TwitterV2({
        consumer_key: this.consumerKey,
        consumer_secret: this.consumerSecret,
      })
    } else {
      log.warn('unable to configure app client for v2 endpoint since not all keys are present')
    }
  }

  getPlaces() {
    return new Promise((resolve) => {
      this.twit.get('trends/available').
        then((resp) => {
          const places = []
          for (const place of resp.data) {
            places.push({
              id: place.woeid,
              name: place.name,
              type: place.placeType.name,
              country: place.country || '',
              countryCode: place.countryCode || '',
              parentId: place.parentid || ''
            })
          }
          resolve(places)
        })
    })
  }

  async getTrendsAtPlace(id) {
    log.info('fetching trends for ' + id)
    const trends = []
    try {
      const resp = await this.twit.get('trends/place', {id: id})
      for (const trend of resp.data[0].trends) {
        trends.push({name: trend.name, count: trend.tweet_volume})
      }
    } catch (e) {
      log.error(`error when fetching trends: ${e}`)
    }
    return trends
  }

  search(opts, cb) {
    log.info('searching for', opts)
    // count is the total number of tweets to return across all API requests
    const count = opts.count || 100

    const params =  {
      ...EVERYTHING,
      "query": opts.q,
      "max_results": 100,
    }

    if (opts.sinceId) {
      params.since_id = opts.sinceId
    }

    if (opts.maxId) {
      params.until_id = opts.maxId
    }

    const endpoint = opts.all ? 'tweets/search/all' : 'tweets/search/recent'

    const recurse = (nextToken, total) => {
      if (nextToken) {
        params.next_token = nextToken
      }
      this.twitterV2.get(endpoint, params).then((resp) => {
        if (resp.data) {
          const tweets = flatten(resp).data
          const newTotal = total + tweets.length
          cb(null, tweets.map(t => this.extractTweet(t)))
            .then(() => {
              if (newTotal < count && resp.meta.next_token) {
                recurse(resp.meta.next_token, newTotal)
              } else {
                cb(null, [])
              }
            })
        } else {
          log.warn(`received search response with no data stanza`)
          cb(null, [])
        }
      })
      .catch(err => {
        log.error(`error during search: ${err}`)
        cb(err, null)
      })
    }

    recurse(null, 0)
  }

  async addFilterRule(value, tag) {
    log.info(`adding filter rule value=${value} tag=${tag}`)
    const payload = {"add": [{value, tag}]}
    const results = this.twitterV2app.post('tweets/search/stream/rules', payload)
    return results
  }

  async getFilterRules() {
    log.info('getting filter rules')
    const results = await this.twitterV2app.get('tweets/search/stream/rules')
    if (results.data) {
      return results.data
    } else {
      return []
    }
  }

  async deleteFilterRule(id) {
    log.info(`deleting filter rule ${id}`)
    const payload = {"delete": {"ids": [id]}}
    const results = this.twitterV2app.post('tweets/search/stream/rules', payload)
    return results
  }

  async filter(cb) {
    log.info('starting filter stream')

    let err = 0
    while (true) {
      try {
        this.stream = this.twitterV2app.stream('tweets/search/stream', EVERYTHING)
        break
      } catch (e) {
        err += 1
        const secs = err ** 2
        log.error(`caught ${e} while connecting to stream, sleeping ${secs}`)
        await timer(secs * 1000)
      }
    }

    try {
      for await (const response of this.stream) {
        if (response.data) {
          // when streaming response.data is an object, instead of a list
          // flatten ensures that response.data is always a list
          // so we want to get the first and only element in the list
          const tweet = flatten(response).data[0]
          const tags = response.matching_rules
            ? response.matching_rules.map(r => r.tag)
            : []

          // if the callback result is not true then we are being told to stop
          const result = await cb(this.extractTweet(tweet), tags)
          if (! result) {
            log.info('callback returned false so stopping filter stream')
            break
          }
        } else {
          log.error(`unexpected filter response: ${JSON.stringify(response)}`)
        }
      }
      // if stream is undefined that means closeFilter() has been called
      if (this.stream) {
        log.error(`stream disconnected normally by Twitter, reconnecting`)
        this.filter(cb)
      }
    } catch (error) {
      log.warn(`stream disconnected with error, retrying`, error)
      this.filter(cb)
    }
  }

  closeFilter() {
    if (this.stream) {
      log.info('closing filter stream')
      this.stream.close()
      this.stream = null
    }
  }

  lookup(o, includes) {
    return {
      ...o,
      ...includes.find(i => o.id === i.id)
    }
  }

  lookupList(list, includes) {
    return list ? list.map(o => this.lookup(o, includes)) : []
  }

  extractTweet(t) {
    let retweet = null
    let quote = null
    for (const ref of t.referenced_tweets || []) {
      if (ref.type == 'retweeted') {
        retweet = ref
      } else if (ref.type === 'quoted') {
        quote = ref
      }
    }

    const hashtags = t.entities && t.entities.hashtags
      ? t.entities.hashtags.map(ht => ht.tag.toLowerCase())
      : []

    const mentions = t.entities && t.entities.mentions
      ? t.entities.mentions.map(m => m.username)
      : []

    let place = null
    if (t.geo && t.geo.place_id) {
      place = {
        name: t.geo.full_name,
        type: t.geo.place_type,
        id: t.geo.place_id,
        country: t.geo.country,
        countryCode: t.geo.country_code,
        boundingBox: t.geo.geo.bbox
      }
    }

    const urls = []
    if (t.entities && t.entities.urls) {
      for (const e of t.entities.urls) {
        const u = url.parse(e.expanded_url)
        // not interested in pointers back to Twitter which
        // happens during quoting
        if (u.hostname === 'twitter.com') {
          continue
        }
        urls.push({
          short: e.url,
          long: e.expanded_url,
          hostname: u.hostname
        })
      }
    }

    const images = []
    const videos = []
    const animatedGifs = []
    if (t.attachments && t.attachments.media) {
      for (const e of t.attachments.media) {
        if (e.type === 'photo') {
          images.push(e.url)
        } else if (e.type === 'video') {
          videos.push(e.preview_image_url)
        } else if (e.type === 'animated_gif') {
          animatedGifs.push(e.preview_image_url)
        }
      }
    }

    return {
      id: t.id,
      created: new Date(t.created_at),
      twitterUrl: `https://twitter.com/${t.author.username}/status/${t.id}`,
      text: decode(t.text),
      language: t.lang,
      client: t.source,
      likeCount: t.public_metrics.like_count,
      retweetCount: t.public_metrics.retweet_count,
      quoteCount: t.public_metrics.quote_count,
      replyCount: t.public_metrics.quote_count,
      retweetId: retweet ? retweet.id : null,
      quoteId: quote ? quote.id : null,
      quote: quote,
      retweet: retweet,
      emojis: emojiMatch.exec(t.text),
      hashtags: hashtags,
      mentions: mentions,
      place: place,
      urls: urls,
      videos: videos,
      images: images,
      animatedGifs: animatedGifs,
      user: {
        id: t.author.id,
        created: new Date(t.author.created_at),
        screenName: t.author.username,
        name: decode(t.author.name),
        description: decode(t.author.description),
        location: decode(t.author.location),
        avatarUrl: t.author.profile_image_url,
        url: t.author.url,
        followersCount: t.author.public_metrics.followers_count,
        friendsCount: t.author.public_metrics.following_count,
        tweetsCount: t.author.public_metrics.tweet_count,
        listedCount: t.author.public_metrics.listed_count,
      },
    }

  }

  async sendTweet(text) {
    const result = await this.twit.post('statuses/update', {
      status: text
    })
    return result.data.id_str
  }

}<|endoftext|>


<commit_before>import https from 'https';

export default class Data {

  constructor(msg, url = null) {
    this.msg = msg;
    this.url = url;
  }

  // return user id, repository and url for request from sliced message.
	sliceMsg() {
		const id = /\.org\/([^\s\/]+)\//.exec(this.msg)[1];
		const repository = /.+\/([^\s\/]+)/.exec(this.msg)[1];

		return {
			id,
			repository,
			url: `https://api.travis-ci.org/repositories/${id}/${repository}.json`
		};
	}

	/**
	 * Make request for .json file and return data from it.
	 * @param {function} cb Callback function to execute, when request is done.
	 */
	req(cb) {

		https.get(this.url, res => {
      let currBuild = 0;
      let prevBuild = 0;
			let str = '';
			res.on('data', data => {
				str += data;
			});
			res.on('end', () => {
				const parsed = JSON.parse(str);

				// If url request dosn't return any data
				if (parsed.file) {
					cb('You have send invalid link, please send valid link', false);
				}

				// If build variables wasn't set and had initiale value
				if (prevBuild === 0 && currBuild === 0) {
					currBuild = prevBuild = parsed.last_build_number;
				}

				// If new build was ended
				if (prevBuild !== currBuild && parsed.last_build_finished_at) {

					// Link address
					const link = `https://travis-ci.org/${parsed.slug}`;

					// Message variables, build: started, ended
					const started = /T(.+)Z/.exec(parsed.last_build_started_at)[1];
					const ended = /T(.+)Z/.exec(parsed.last_build_finished_at)[1];

					// Reassigning build variables
					currBuild = parsed.last_build_number;
					prevBuild = parsed.last_build_number;

					// Callback function
					cb(`Hi, your build at ${link} repository just has ended. \nYour build ${parsed.last_build_status === 0 ? 'completed successfully' : 'failed'}. \nBuild number was ${parsed.last_build_number}. \nYour build started at ${started} and finished at ${ended}. Link to build: ${link}/builds/${parsed.last_build_id}`, true);
				} else if (!parsed.last_build_finished_at) {

					// If build is currently running
					prevBuild = parsed.last_build_number - 1;
				}
			});
		}).end();
	}
}
<commit_msg>Update index.js<commit_after>import https from 'https';

export default class Data {

  constructor(msg, url = null) {
    this.msg = msg;
    this.url = url;
  }

  // return user id, repository and url for request from sliced message.
	sliceMsg() {
		const id = /\.org\/([^\s]+)\//.exec(this.msg)[1];
		const repository = /.+\/([^\s]+)/.exec(this.msg)[1];

		return {
			id,
			repository,
			url: `https://api.travis-ci.org/repositories/${id}/${repository}.json`
		};
	}

	/**
	 * Make request for .json file and return data from it.
	 * @param {function} cb Callback function to execute, when request is done.
	 */
	req(cb) {

		https.get(this.url, res => {
      let currBuild = 0;
      let prevBuild = 0;
			let str = '';
			res.on('data', data => {
				str += data;
			});
			res.on('end', () => {
				const parsed = JSON.parse(str);

				// If url request dosn't return any data
				if (parsed.file) {
					cb('You have send invalid link, please send valid link', false);
				}

				// If build variables wasn't set and had initiale value
				if (prevBuild === 0 && currBuild === 0) {
					currBuild = prevBuild = parsed.last_build_number;
				}

				// If new build was ended
				if (prevBuild !== currBuild && parsed.last_build_finished_at) {

					// Link address
					const link = `https://travis-ci.org/${parsed.slug}`;

					// Message variables, build: started, ended
					const started = /T(.+)Z/.exec(parsed.last_build_started_at)[1];
					const ended = /T(.+)Z/.exec(parsed.last_build_finished_at)[1];

					// Reassigning build variables
					currBuild = parsed.last_build_number;
					prevBuild = parsed.last_build_number;

					// Callback function
					cb(`Hi, your build at ${link} repository just has ended. \nYour build ${parsed.last_build_status === 0 ? 'completed successfully' : 'failed'}. \nBuild number was ${parsed.last_build_number}. \nYour build started at ${started} and finished at ${ended}. Link to build: ${link}/builds/${parsed.last_build_id}`, true);
				} else if (!parsed.last_build_finished_at) {

					// If build is currently running
					prevBuild = parsed.last_build_number - 1;
				}
			});
		}).end();
	}
}
<|endoftext|>


<commit_before>/**
 * Created by prandutt on 2/19/2015.
 */

angular.module('signalr', []).provider('signalrService', function () {

    var configuration;
    this.config = function (config) {
        configuration = config;
    };

    this.$get = ['$log', '$timeout', '$q', function ($log, $timeout, $q) {
        return new signalrService(configuration, $log, $timeout, $q);
    }];

});

function signalrService(configuration, $log, $timeout, $q) {

    if (!(configuration && configuration.register)) {
        $log.log('Invalid config');
        return;
    }

    var signalRhubs = $.hubConnection();

    if (!signalRhubs) {
        $log.log('SignalR is not referenced.');
        return;
    }

    signalRhubs.logging = configuration.logging || false;

    angular.forEach(configuration.register, function (key, value) {
        var x = signalRhubs.createHubProxy(key.hub);
        x.on(key.method, function (data) {
        });
    });

    signalRhubs.connectionSlow(function () {
        $log.warn('We are currently experiencing difficulties with the connection.')
    });

    signalRhubs.error(function (error) {
        $log.error('SignalR error: ' + error)
    });

    $(signalRhubs).bind("onDisconnect", function (e, data) {
        $timeout(function () {
            $log.warn('Signalr Connection dead.')
        }, 10000);
    });

    this.invoke = function (hubName, serverFunction, paramObject) {

        var def = $q.defer();

        var hubProxy = signalRhubs.createHubProxy(hubName)

        signalRhubs.start().done(function () {

            if (paramObject) {

                hubProxy.invoke(serverFunction, paramObject).done(function (data) {
                    if(data)
                        def.resolve(data);
                    else
                        def.resolve();

                }).fail(function (error) {
                    $log.error('SignalR error: ' + error)
                    def.reject();
                });
            }
            else {
                hubProxy.invoke(serverFunction).done(function (data) {
                    def.resolve(data);
                }).fail(function (error) {
                    $log.error('SignalR error: ' + error)
                    def.reject();
                });
            }
        }).fail(function () {
            $log.error('Failed to start signalR')
            def.reject();
        });
        return def.promise;
    }

    this.on = function (hubName, clientSubscribeFunction, func) {
        var hubProxy = signalRhubs.createHubProxy(hubName)
        hubProxy.on(clientSubscribeFunction, func);
    }

}
<commit_msg>event for signalr state change<commit_after>/**
 * Created by prandutt on 2/19/2015.
 */

angular.module('signalr', []).provider('signalrService', function () {

    var configuration;
    this.config = function (config) {
        configuration = config;
    };

    this.$get = ['$log', '$timeout', '$q', '$rootScope', function ($log, $timeout, $q, $rootScope) {
        return new signalrService(configuration, $log, $timeout, $q,$rootScope);
    }];

});

function signalrService(configuration, $log, $timeout, $q, $rootScope) {

    if (!(configuration && configuration.register)) {
        $log.log('Invalid config');
        return;
    }

    var signalRhubs = $.hubConnection();

    if (!signalRhubs) {
        $log.log('SignalR is not referenced.');
        return;
    }

    signalRhubs.logging = configuration.logging || false;

    angular.forEach(configuration.register, function (key, value) {
        var x = signalRhubs.createHubProxy(key.hub);
        x.on(key.method, function (data) {
        });
    });

    signalRhubs.stateChanged(function (change) {
        $rootScope.$emit('signalrStateChange', change.newState);

    });

    signalRhubs.connectionSlow(function () {
        $log.warn('We are currently experiencing difficulties with the connection.')
    });

    signalRhubs.error(function (error) {
        $log.error('SignalR error: ' + error)
    });

    $(signalRhubs).bind("onDisconnect", function (e, data) {
        $timeout(function () {
            $log.warn('Signalr Connection dead.')
        }, 10000);
    });

    this.invoke = function (hubName, serverFunction, paramObject) {

        var def = $q.defer();

        var hubProxy = signalRhubs.createHubProxy(hubName)

        signalRhubs.start().done(function () {

            if (paramObject) {

                hubProxy.invoke(serverFunction, paramObject).done(function (data) {
                    if(data)
                        def.resolve(data);
                    else
                        def.resolve();

                }).fail(function (error) {
                    $log.error('SignalR error: ' + error)
                    def.reject();
                });
            }
            else {
                hubProxy.invoke(serverFunction).done(function (data) {
                    def.resolve(data);
                }).fail(function (error) {
                    $log.error('SignalR error: ' + error)
                    def.reject();
                });
            }
        }).fail(function () {
            $log.error('Failed to start signalR')
            def.reject();
        });
        return def.promise;
    }

    this.on = function (hubName, clientSubscribeFunction, func) {
        var hubProxy = signalRhubs.createHubProxy(hubName)
        hubProxy.on(clientSubscribeFunction, func);
    }

}
<|endoftext|>


<commit_before>/*
* Flow (NextGen) v0.X
* http://github.com/bemson/Flow/
*
* Copyright 2011, Bemi Faison
* Released under the MIT License
*/
!function (window, undefined) {
  // init vars
  var sig = {}, // private signature object for priviledged access
    pkgDefs = [], // collection of package-definitions
    pkgsIdx = {}, // name index of package-definitions
    genStates = new genData( // spawn state generator
      function (name, value, parent, dataset, flags) {
        // init vars
        var state = this, // alias state
          isInvalid = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this an invalid key
            // init vars
            var hdlr = pkgDef.pkg.invalidKey; // get invalidKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          }),
          isData = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this a data key
            // init vars
            var hdlr = pkgDef.pkg.dataKey; // get dataKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          });
        // if this key is invalid or flagged as data...
        if (isInvalid || isData) {
          // exclude from dataset and don't scan
          flags.exclude = 1;
          // don't scan this value
          flags.scanValue = 0;
          // if valid data, capture in parent data
          if (isData && !isInvalid) parent.data[name] = value;
        } else { // otherwise, when this key is not invalid or data...
          // set default property values to undefined (presence reduces prototype property lookups)
          state.inContext = state.parentIndex = state.previousIndex = state.nextIndex = state.firstChildIndex = undefined;
          // capture index of this item once added
          state.index = dataset.length + 1;
          // capture depth
          state.depth = parent ? parent.depth + 1 : 1; // start depth at 1, since _flow state will be prepended later
          // set name
          state.name = parent ? name : '_root';
          // init data property - holds any attributes of this state
          state.data = {};
          // start or extend parent location
          state.location = parent ? parent.location + name + '/' : '//';
          // init child collection
          state.children = [];
          // if there is a parent state...
          if (parent) {
            // set parent index
            state.parentIndex = parent.index;
            // if there are no children, set first child index
            if (!parent.children.length) parent.firstChildIndex = state.index;
            // capture the index of this state in the parent's child collection
            state.childIndex = parent.children.push(state.index) - 1;
            // set this state as the last child of the parent
            parent.lastChildIndex = state.index;
            // if not the first child...
            if (state.childIndex) {
              // reference index of previous state
              state.previousIndex = parent.children[state.childIndex - 1];
              // reference index of this state in the previous state
              dataset[state.previousIndex - 1].nextIndex = state.index;
            }
          }
        }
      }
    );
  // define prototype base for FlowAPI instances
  function ProxyModel() {}
  // create
  function definePackage(name) {
    // package returns the private instance of it's public proxy
    function pkg(pxy) {
      // init vars
      var flow = pxy && pxy.toString(sig); // pass secret to pxy's toString
      // return the package instance with this name
      return typeof flow === 'object' && (flow.pkgs.filter(function (pkgInst) {
        return pkgInst.name === name
      })[0] || {}).pkg
    }
    // set default static properties
    pkg.init = pkg.dataKey = pkg.invalidKey = pkg.onStart = pkg.onEnd = pkg.onFinish = pkg.onTraverse = 0;
    // define new prototype-model for this package
    function Model() {}
    // chain existing FlowModel to this model
    Model.prototype = new ProxyModel();
    // define new FlowModel from this extended chain
    ProxyModel = Model;
    // expose the model's prototype for adding API methods
    pkg.api = Model.prototype;
    // define package definition object, to capture and index this package and it's model
    pkgsIdx[name] = pkgDefs.push({
      name: name,
      pkg: pkg,
      model: Model
    }) - 1;
    // return package
    return pkg;
  }
  function getStates(program) {
    // init vars
    var states = genStates(program); // parse initial states
    // set parent and childIndex of root
    states[0].parentIndex = states[0].childIndex = 0;
    // prepend flow state
    states.unshift(genStates()[0]);
    // reference index of root as child of flow
    states[0].children.push(1);
    // set name
    states[0].name = '_flow';
    // set index
    states[0].index = 0;
    // set depth
    states[0].depth = 0;
    // set location
    states[0].location = '..//';
    // reference the first and last child index
    states[0].firstChildIndex = states[0].lastChildIndex = 1;
    // return states
    return states;
  }
  function Flow(program, proxy) {
    // init vars
    var flow = this; // alias this instance
    // states for this program
    flow.states = getStates(program);
    // define shared pkg api
    flow.shared = {
      // index of the current program state
      currentIndex: 0,
      // index of the target program state
      targetIndex: 0,
      // define scoped call to direct this flow
      go: function (idx) {
        // set target to the state at the given index
        flow.target = flow.states[idx];
        // set target index
        flow.shared.targetIndex = idx;
        // set internal stop flag
        flow.stop = 0;
        // traverse towards the target
        return flow.go();
      },
      // define scoped call to stop this flow
      stop: function () {
        // set internal stop flag
        flow.stop = 1;
        // flag success in setting the stop flag
        return true;
      }
    };
    // init current state reference
    flow.current = flow.states[0];
    // init target state and looping flags
    flow.target = flow.looping = 0;
    // define a pkg instance for each definition...
    flow.pkgs = pkgDefs.map(function (pkgDef) {
      // init vars
      var pkgInst = { // init pkg instance
        // capture package name for lookups
        name: pkgDef.name
      };
      // define base package
      function pkgBase() {}
      // extend the base package prototype
      pkgBase.prototype = new pkgDef.pkg();
      // set pkg to new pkgBase
      pkgInst.pkg = new pkgBase();
      // clone states from this flow
      pkgInst.pkg.states = flow.states.map(function (state) {
        // define anonymous constructor
        function copyState() {};
        // use state as prototype for this object
        copyState.prototype = state;
        // return copy of this state
        return new copyState();
      });
      // if there is a package initialization function...
      if (pkgDef.pkg.init) {
        // initialize package instance according to the definition's function
        pkgDef.pkg.init.call(pkgInst.pkg);
      }
      // add flow and proxy properties
      // expose shared api to this package instance
      pkgInst.pkg.flow = flow.shared;
      // expose public proxy to this package instance
      pkgInst.pkg.proxy = proxy;
      // return the pkgInst
      return pkgInst;
    });
  }
  Flow.prototype = {
    // head towards the current target
    go: function () {
      // init vars
      var flow = this, // alias self
        states = flow.states, // alias states (for minification & performance)
        shared = flow.shared, // alias shared (for minification & performance)
        dir, // direction of traversal movement
        curState = flow.current, // alias the current state (for minification & performance)
        nextIsEvent = 0, // flag when the nextInt is an event (when 0) or state index (when 1)
        nextInt = 0, // integer representing the state index or event type
        firedStop, // flag when we've fired the stop event
        firedFinish; // flag when we've fired the finish event
      // if already looping...
      if (flow.looping) {
        // flag true if there is a current target
        return !!flow.target;
      }
      // flag that this flow is looping
      flow.looping = 1;
      // fire start event
      flow.fire('Start');
      // while looping...
      while (flow.looping) {
        // if there is a target and we haven't stopped...
        if (flow.target && !flow.stop) {
          // reset firedStop and firedFinish flags
          firedStop = firedFinish = 0;
          // get traversal direction
          dir = flow.target.index - curState.index;
          // if going forwards or backwards...
          if (dir) {
            // if going forward on the _flow or _root state, or the target path contains the current path...
            if ((dir > 0 && curState.index < 2) || !flow.target.location.indexOf(curState.location)) {
              // if already in context...
              if (curState.inContext) {
                // flag that we're switching states
                nextIsEvent = 0;
                // target the first child
                nextInt = curState.firstChildIndex;
              } else { // otherwise, if not in context...
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to in event
                nextInt = 1;
                // flag that we're in the current state
                curState.inContext = 1;
              }
            } else { // otherwise, if the target path is not in the current path...
              // if in the context of the current state...
              if (curState.inContext) {
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to out event
                nextInt = 2;
                // flag that we're out of the current state
                curState.inContext = 0;
              } else { // otherwise, when out of this state...
                // if the current state's parent is an ancestor of the target state...
                if (flow.target.location.indexOf(states[curState.parentIndex].location)) {
                  // set direction to backwards
                  dir = -1;
                }
                // predict next event based on the direction
                nextInt = dir < 0 ? 4 : 3;
                // if the last event was out, or matches the calculated one...
                if (curState.lastEvent === 2 || curState.lastEvent === nextInt) {
                  // flag that we're changing states
                  nextIsEvent = 0;
                  // go forward, backward, or up based on direction
                  nextInt = dir > 0 ? curState.nextIndex : (curState.previousIndex || curState.parentIndex);
                } else { // otherwise, when the last event was not out and won't be repeated...
                  // flag that we're doing an event (the one previously calculated)
                  nextIsEvent = 1;
                }
              }
            }
          } else { // otherwise, when on the target state...
            // flag that we're doing an event
            nextIsEvent = 1;
            // set event to on or in, based on the current context
            nextInt = curState.inContext ? 0 : 1;
            // if already in context...
            if (curState.inContext) {
              // clear internal target
              flow.target = 0;
              // clear shared target (set to negative one)
              shared.targetIndex = -1;
            }
            // set context to in
            curState.inContext = 1;
          }
          // if doing an event...
          if (nextIsEvent) {
            // capture last event
            curState.lastEvent = nextInt;
            // fire traverse event with the resolved next target
            flow.fire('Traverse', [nextInt]);
          } else { // otherwise, when changing the current state...
            // reset lastEvent flag from the current state
            curState.lastEvent = 0;
            // set internal current state
            curState = flow.current = states[nextInt];
            // set shared target
            shared.currentIndex = nextInt;
          }
        } else if (!firedStop && (flow.stop || !flow.target)) { // or, when stopped and we did not fire the stop event and we've stopped...
          // note that we've fired the stop event
          firedStop = 1;
          // clear firedFinish flag
          firedFinish = 0;
          // fire stop event
          flow.fire('Stop');
        } else if (firedStop && !firedFinish && !flow.target) { // or, when we've fired stop, haven't fired finish, and there is no target...
          // set firedFinish flag
          firedFinish = 1;
          // fire finish event
          flow.fire('Finish');
        } else { // (otherwise), when none of these conditions are met...
          // flag that we're done looping
          flow.looping = 0;
        }
      }
      // return the index of the current state
      return curState.index;
    },
    // fire package event
    fire: function (evtName, args) {
      // prepend evtName with "on" prefix
      evtName = 'on' + evtName;
      // use args or array - assumes args is an array
      args = args || [];
      // with each package instance...
      this.pkgs.forEach(function (pkgInst) {
        // init vars
        var callback = pkgDefs[pkgsIdx[pkgInst.name]].pkg[evtName]; // get callback from this package instance's definition
        // if the callback is valid...
        if (callback) {
          // execute call back in scope of the package instance, with given args
          callback.apply(pkgInst.pkg, args);
        }
      });
    }
  };

  function FlowAPI(program) {
    // init vars
    var apiPkgs = {}, // define pkgs collection for this Flow
      flowProxy = new (getFlowProxy()), // create initial flow proxy
      flow = new Flow(program, flowProxy); // define (private) flow, and pass flowProxy for any packages
    // faux toString method, for accessing the private flow
    function proxyToString(key) { // faux toString method
      // return corresponding flow or default toString result
      return key === sig ? flow : Object.prototype.toString.apply(this, arguments);
    }
    // return an API container for the private flow
    function getFlowProxy() {
      // return anonymous pointer to the private flow
      function api() {
        // reference pkgs for targeted method calls
        this.pkgs = apiPkgs;
        // add custom
        this.toString = proxyToString;
      }
      // use the ProxyModel as the default prototype
      api.prototype = new ProxyModel();
      // return anonymous proxy instance
      return api;
    }
    // with each package-definition...
    pkgDefs.forEach(function (pkgDef) {
      // init vars
      var apiProxy = getFlowProxy(); // get unique proxy constructor
      // prototype this proxy for this package
      apiProxy.prototype = new pkgDef.model();
      // add package methods to this proxy's pkgs object
      apiPkgs[pkgDef.name] = new apiProxy();
    });
    // return the flow proxy
    return flowProxy;
  }
  // define object pointing to flow
  // set prototype to given model
  FlowAPI.pkg = function (name) {
    // if arguments passed...
    if (arguments.length) {
      // if given a valid name to resolve...
      if (typeof name === 'string' && /\w/.test(name)) {
        // if no package has this name, create new package definition
        if (!pkgsIdx.hasOwnProperty(name)) definePackage(name);
        // return the package definition's pkg function
        return pkgDefs[pkgsIdx[name]].pkg;
      }
      // (otherwise) return false - fire error in the future?
      return !1;
    }
    // (otherwise) return list of package names
    return pkgDefs.map(function (pkgDef) {
      // extract the package name
      return pkgDef.name;
    });
  }
  window.Flow = FlowAPI;
}(this);<commit_msg>Updated to work with genData-0.8<commit_after>/*
* Flow (NextGen) v0.X
* http://github.com/bemson/Flow/
*
* Copyright 2011, Bemi Faison
* Released under the MIT License
*/
!function (window, undefined) {
  // init vars
  var sig = {}, // private signature object for priviledged access
    pkgDefs = [], // collection of package-definitions
    pkgsIdx = {}, // name index of package-definitions
    genStates = new genData( // spawn state generator
      function (name, value, parent, dataset, flags) {
        // init vars
        var state = this, // alias state
          isInvalid = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this an invalid key
            // init vars
            var hdlr = pkgDef.pkg.invalidKey; // get invalidKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          }),
          isData = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this a data key
            // init vars
            var hdlr = pkgDef.pkg.dataKey; // get dataKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          });
        // if this key is invalid or flagged as data...
        if (isInvalid || isData) {
          // exclude from dataset and don't scan
          flags.omit = 1;
          // don't scan this value
          flags.scan = 0;
          // if valid data, capture in parent data
          if (isData && !isInvalid) parent.data[name] = value;
        } else { // otherwise, when this key is not invalid or data...
          // set default property values to undefined (presence reduces prototype property lookups)
          state.inContext = state.parentIndex = state.previousIndex = state.nextIndex = state.firstChildIndex = undefined;
          // capture index of this item once added
          state.index = dataset.length + 1;
          // capture depth
          state.depth = parent ? parent.depth + 1 : 1; // start depth at 1, since _flow state will be prepended later
          // set name
          state.name = parent ? name : '_root';
          // init data property - holds any attributes of this state
          state.data = {};
          // start or extend parent location
          state.location = parent ? parent.location + name + '/' : '//';
          // init child collection
          state.children = [];
          // if there is a parent state...
          if (parent) {
            // set parent index
            state.parentIndex = parent.index;
            // if there are no children, set first child index
            if (!parent.children.length) parent.firstChildIndex = state.index;
            // capture the index of this state in the parent's child collection
            state.childIndex = parent.children.push(state.index) - 1;
            // set this state as the last child of the parent
            parent.lastChildIndex = state.index;
            // if not the first child...
            if (state.childIndex) {
              // reference index of previous state
              state.previousIndex = parent.children[state.childIndex - 1];
              // reference index of this state in the previous state
              dataset[state.previousIndex - 1].nextIndex = state.index;
            }
          }
        }
      }
    );
  // define prototype base for FlowAPI instances
  function ProxyModel() {}
  // create
  function definePackage(name) {
    // package returns the private instance of it's public proxy
    function pkg(pxy) {
      // init vars
      var flow = pxy && pxy.toString(sig); // pass secret to pxy's toString
      // return the package instance with this name
      return typeof flow === 'object' && (flow.pkgs.filter(function (pkgInst) {
        return pkgInst.name === name
      })[0] || {}).pkg
    }
    // set default static properties
    pkg.init = pkg.dataKey = pkg.invalidKey = pkg.onStart = pkg.onEnd = pkg.onFinish = pkg.onTraverse = 0;
    // define new prototype-model for this package
    function Model() {}
    // chain existing FlowModel to this model
    Model.prototype = new ProxyModel();
    // define new FlowModel from this extended chain
    ProxyModel = Model;
    // expose the model's prototype for adding API methods
    pkg.api = Model.prototype;
    // define package definition object, to capture and index this package and it's model
    pkgsIdx[name] = pkgDefs.push({
      name: name,
      pkg: pkg,
      model: Model
    }) - 1;
    // return package
    return pkg;
  }
  function getStates(program) {
    // init vars
    var states = genStates(program); // parse initial states
    // set parent and childIndex of root
    states[0].parentIndex = states[0].childIndex = 0;
    // prepend flow state
    states.unshift(genStates()[0]);
    // reference index of root as child of flow
    states[0].children.push(1);
    // set name
    states[0].name = '_flow';
    // set index
    states[0].index = 0;
    // set depth
    states[0].depth = 0;
    // set location
    states[0].location = '..//';
    // reference the first and last child index
    states[0].firstChildIndex = states[0].lastChildIndex = 1;
    // return states
    return states;
  }
  function Flow(program, proxy) {
    // init vars
    var flow = this; // alias this instance
    // states for this program
    flow.states = getStates(program);
    // define shared pkg api
    flow.shared = {
      // index of the current program state
      currentIndex: 0,
      // index of the target program state
      targetIndex: 0,
      // define scoped call to direct this flow
      go: function (idx) {
        // set target to the state at the given index
        flow.target = flow.states[idx];
        // set target index
        flow.shared.targetIndex = idx;
        // set internal stop flag
        flow.stop = 0;
        // traverse towards the target
        return flow.go();
      },
      // define scoped call to stop this flow
      stop: function () {
        // set internal stop flag
        flow.stop = 1;
        // flag success in setting the stop flag
        return true;
      }
    };
    // init current state reference
    flow.current = flow.states[0];
    // init target state and looping flags
    flow.target = flow.looping = 0;
    // define a pkg instance for each definition...
    flow.pkgs = pkgDefs.map(function (pkgDef) {
      // init vars
      var pkgInst = { // init pkg instance
        // capture package name for lookups
        name: pkgDef.name
      };
      // define base package
      function pkgBase() {}
      // extend the base package prototype
      pkgBase.prototype = new pkgDef.pkg();
      // set pkg to new pkgBase
      pkgInst.pkg = new pkgBase();
      // clone states from this flow
      pkgInst.pkg.states = flow.states.map(function (state) {
        // define anonymous constructor
        function copyState() {};
        // use state as prototype for this object
        copyState.prototype = state;
        // return copy of this state
        return new copyState();
      });
      // if there is a package initialization function...
      if (pkgDef.pkg.init) {
        // initialize package instance according to the definition's function
        pkgDef.pkg.init.call(pkgInst.pkg);
      }
      // add flow and proxy properties
      // expose shared api to this package instance
      pkgInst.pkg.flow = flow.shared;
      // expose public proxy to this package instance
      pkgInst.pkg.proxy = proxy;
      // return the pkgInst
      return pkgInst;
    });
  }
  Flow.prototype = {
    // head towards the current target
    go: function () {
      // init vars
      var flow = this, // alias self
        states = flow.states, // alias states (for minification & performance)
        shared = flow.shared, // alias shared (for minification & performance)
        dir, // direction of traversal movement
        curState = flow.current, // alias the current state (for minification & performance)
        nextIsEvent = 0, // flag when the nextInt is an event (when 0) or state index (when 1)
        nextInt = 0, // integer representing the state index or event type
        firedStop, // flag when we've fired the stop event
        firedFinish; // flag when we've fired the finish event
      // if already looping...
      if (flow.looping) {
        // flag true if there is a current target
        return !!flow.target;
      }
      // flag that this flow is looping
      flow.looping = 1;
      // fire start event
      flow.fire('Start');
      // while looping...
      while (flow.looping) {
        // if there is a target and we haven't stopped...
        if (flow.target && !flow.stop) {
          // reset firedStop and firedFinish flags
          firedStop = firedFinish = 0;
          // get traversal direction
          dir = flow.target.index - curState.index;
          // if going forwards or backwards...
          if (dir) {
            // if going forward on the _flow or _root state, or the target path contains the current path...
            if ((dir > 0 && curState.index < 2) || !flow.target.location.indexOf(curState.location)) {
              // if already in context...
              if (curState.inContext) {
                // flag that we're switching states
                nextIsEvent = 0;
                // target the first child
                nextInt = curState.firstChildIndex;
              } else { // otherwise, if not in context...
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to in event
                nextInt = 1;
                // flag that we're in the current state
                curState.inContext = 1;
              }
            } else { // otherwise, if the target path is not in the current path...
              // if in the context of the current state...
              if (curState.inContext) {
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to out event
                nextInt = 2;
                // flag that we're out of the current state
                curState.inContext = 0;
              } else { // otherwise, when out of this state...
                // if the current state's parent is an ancestor of the target state...
                if (flow.target.location.indexOf(states[curState.parentIndex].location)) {
                  // set direction to backwards
                  dir = -1;
                }
                // predict next event based on the direction
                nextInt = dir < 0 ? 4 : 3;
                // if the last event was out, or matches the calculated one...
                if (curState.lastEvent === 2 || curState.lastEvent === nextInt) {
                  // flag that we're changing states
                  nextIsEvent = 0;
                  // go forward, backward, or up based on direction
                  nextInt = dir > 0 ? curState.nextIndex : (curState.previousIndex || curState.parentIndex);
                } else { // otherwise, when the last event was not out and won't be repeated...
                  // flag that we're doing an event (the one previously calculated)
                  nextIsEvent = 1;
                }
              }
            }
          } else { // otherwise, when on the target state...
            // flag that we're doing an event
            nextIsEvent = 1;
            // set event to on or in, based on the current context
            nextInt = curState.inContext ? 0 : 1;
            // if already in context...
            if (curState.inContext) {
              // clear internal target
              flow.target = 0;
              // clear shared target (set to negative one)
              shared.targetIndex = -1;
            }
            // set context to in
            curState.inContext = 1;
          }
          // if doing an event...
          if (nextIsEvent) {
            // capture last event
            curState.lastEvent = nextInt;
            // fire traverse event with the resolved next target
            flow.fire('Traverse', [nextInt]);
          } else { // otherwise, when changing the current state...
            // reset lastEvent flag from the current state
            curState.lastEvent = 0;
            // set internal current state
            curState = flow.current = states[nextInt];
            // set shared target
            shared.currentIndex = nextInt;
          }
        } else if (!firedStop && (flow.stop || !flow.target)) { // or, when stopped and we did not fire the stop event and we've stopped...
          // note that we've fired the stop event
          firedStop = 1;
          // clear firedFinish flag
          firedFinish = 0;
          // fire stop event
          flow.fire('Stop');
        } else if (firedStop && !firedFinish && !flow.target) { // or, when we've fired stop, haven't fired finish, and there is no target...
          // set firedFinish flag
          firedFinish = 1;
          // fire finish event
          flow.fire('Finish');
        } else { // (otherwise), when none of these conditions are met...
          // flag that we're done looping
          flow.looping = 0;
        }
      }
      // return the index of the current state
      return curState.index;
    },
    // fire package event
    fire: function (evtName, args) {
      // prepend evtName with "on" prefix
      evtName = 'on' + evtName;
      // use args or array - assumes args is an array
      args = args || [];
      // with each package instance...
      this.pkgs.forEach(function (pkgInst) {
        // init vars
        var callback = pkgDefs[pkgsIdx[pkgInst.name]].pkg[evtName]; // get callback from this package instance's definition
        // if the callback is valid...
        if (callback) {
          // execute call back in scope of the package instance, with given args
          callback.apply(pkgInst.pkg, args);
        }
      });
    }
  };

  function FlowAPI(program) {
    // init vars
    var apiPkgs = {}, // define pkgs collection for this Flow
      flowProxy = new (getFlowProxy()), // create initial flow proxy
      flow = new Flow(program, flowProxy); // define (private) flow, and pass flowProxy for any packages
    // faux toString method, for accessing the private flow
    function proxyToString(key) { // faux toString method
      // return corresponding flow or default toString result
      return key === sig ? flow : Object.prototype.toString.apply(this, arguments);
    }
    // return an API container for the private flow
    function getFlowProxy() {
      // return anonymous pointer to the private flow
      function api() {
        // reference pkgs for targeted method calls
        this.pkgs = apiPkgs;
        // add custom
        this.toString = proxyToString;
      }
      // use the ProxyModel as the default prototype
      api.prototype = new ProxyModel();
      // return anonymous proxy instance
      return api;
    }
    // with each package-definition...
    pkgDefs.forEach(function (pkgDef) {
      // init vars
      var apiProxy = getFlowProxy(); // get unique proxy constructor
      // prototype this proxy for this package
      apiProxy.prototype = new pkgDef.model();
      // add package methods to this proxy's pkgs object
      apiPkgs[pkgDef.name] = new apiProxy();
    });
    // return the flow proxy
    return flowProxy;
  }
  // define object pointing to flow
  // set prototype to given model
  FlowAPI.pkg = function (name) {
    // if arguments passed...
    if (arguments.length) {
      // if given a valid name to resolve...
      if (typeof name === 'string' && /\w/.test(name)) {
        // if no package has this name, create new package definition
        if (!pkgsIdx.hasOwnProperty(name)) definePackage(name);
        // return the package definition's pkg function
        return pkgDefs[pkgsIdx[name]].pkg;
      }
      // (otherwise) return false - fire error in the future?
      return !1;
    }
    // (otherwise) return list of package names
    return pkgDefs.map(function (pkgDef) {
      // extract the package name
      return pkgDef.name;
    });
  }
  window.Flow = FlowAPI;
}(this);<|endoftext|>


<commit_before>'use strict';
var _ = require('underscore');
var Module = require('module-js');
var Dropdown = require('./dropdown');
var InputField = require('./input-field');
var Checkbox = require('./checkbox');
var ButtonToggle = require('./button-toggle');
var SubmitButton = require('./submit-button');

require('element-kit');

/**
 * The function that fires when the input value changes
 * @callback Form~onValueChange
 * @param {string} value - The new value
 * @param {HTMLElement} element - The form element
 * @param {HTMLElement} uIElement - The ui-version of the form element
 */

/**
 * The function that fires to give users opportunity to return a custom set of options on a per-element basis
 * @callback Form~onGetOptions
 * @param {HTMLElement|Array} element(s) - The element (or if radio buttons, an array of elements) on which to use the custom options
 * @returns {Object} Return the custom set of options
 */

/**
 * The function that fires when the submit button is clicked
 * @callback Form~onSubmitButtonClick
 * @returns {Event} Returns the click event
 */

/**
 * Utility class for form elements.
 * @class Form
 */
var Form = Module.extend({

    /**
     * Sets up the form.
     * @param {object} options - The options
     * @param {HTMLFormElement} [options.el] - The form element
     * @param {HTMLFormElement} options.el - The form element
     * @param {Form~onValueChange} [options.onValueChange] - A callback function that fires when the value of any form element changes
     * @param {Function} [options.onGetOptions] - Function callback that is fired upon instantiation to provide custom options
     * @param {string} [options.dropdownClass] - The css class used to query the set of dropdown elements that should be included
     * @param {string} [options.checkboxClass] - The css class used to query the set of checkbox elements that should be included
     * @param {string} [options.inputFieldClass] - The css class used to query the set of text input elements that should be included
     * @param {string} [options.buttonToggleClass] - The css class used to query the set of radio button elements that should be included
     * @param {string} [options.submitButtonClass] - The css class used to query the submit button
     * @param {string} [options.submitButtonDisabledClass] - The class that will be applied to the submit button when its disabled
     * @param {string} [options.onSubmitButtonClick] - Function that is called when the submit button is clicked
     */
    initialize: function (options) {

        this.options = _.extend({
            el: null,
            onValueChange: null,
            onGetOptions: null,
            dropdownClass: null,
            checkboxClass: null,
            inputFieldClass: null,
            buttonToggleClass: null,
            submitButtonClass: null,
            submitButtonDisabledClass: null,
            onSubmitButtonClick: null
        }, options);

        // okay to cache here because its a "live" html collection -- yay!
        this.formEls = this.options.el.elements;

        this._formInstances = [];
        this._moduleCount = 0;
        Module.prototype.initialize.call(this, this.options);
    },

    /**
     * Returns a mapping of ids to their associated form option and selector.
     */
    _getSelectorMap: function () {
        return {
            dropdown: {
                option: this.options.dropdownClass,
                selector: 'select',
                tag: 'select'
            },
            checkbox: {
                option: this.options.checkboxClass,
                tag: 'input',
                types: ['checkbox']
            },
            input: {
                option: this.options.inputFieldClass,
                tag: 'input',
                types: ['password', 'email', 'number', 'text']
            },
            radio: {
                option: this.options.buttonToggleClass,
                tag: 'input',
                types: ['radio']
            }
        }
    },

    /**
     * Sets up the form and instantiates all necessary element classes.
     */
    setup: function () {
        var submitButtonEl = this.options.el.getElementsByClassName(this.options.submitButtonClass)[0];

        this._setupInstances(this._getInstanceEls('dropdown'), Dropdown);
        this._setupInstances(this._getInstanceEls('checkbox'), Checkbox);
        this._setupInstances(this._getInstanceEls('input'), InputField);

        // group radio button toggles by name before instantiating
        var radios = this._getInstanceEls('radio');
        _.each(this.mapElementsByAttribute(radios, 'name'), function (els) {
            this._setupInstance(els, ButtonToggle, {}, 'inputs');
        }, this);

        if (submitButtonEl) {
            this.subModules.submitButton = new SubmitButton({
                el: submitButtonEl,
                disabledClass: this.options.submitButtonDisabledClass,
                onClick: this.options.onSubmitButtonClick
            });
        }
    },

    /**
     * Gets the matching form elements, based on the supplied type.
     * @param {string} type - The type identifier (i.e. "dropdown", "checkbox", "input")
     * @returns {Array|HTMLCollection} Returns an array of matching elements
     * @private
     */
    _getInstanceEls: function (type) {
        var formEl = this.options.el,
            elements = [],
            map = this._getSelectorMap();

        map = map[type] || {};

        // we are strategically grabbing elements by "tagName" to ensure we have a LIVE HTMLCollection
        // instead of an ineffective, non-live NodeList (i.e. querySelector), can we say, "less state management"!

        if (map.option) {
            elements = formEl.getElementsByClassName(map.option);
        } else if (map.types) {
            map.types.forEach(function (val) {
                (this.mapElementsByAttribute(this.formEls, 'type')[val] || []).forEach(function (el) {
                    elements.push(el);
                });
            }, this);
        } else if (map.tag) {
            elements = formEl.getElementsByTagName(map.tag);
        }
        return elements;
    },

    /**
     * Creates a single instance of a class for each of the supplied elements.
     * @param {HTMLCollection|Array} elements - The set of elements to instance the class on
     * @param {Function} View - The class to instantiate
     * @param {Object} [options] - The options to be passed to instantiation
     * @param {string} [elKey] - The key to use as the "el"
     * @private
     */
    _setupInstances: function (elements, View, options, elKey) {
        var count = elements.length,
            i;
        if (count) {
            for (i = 0; i < count; i++) {
                this._setupInstance(elements[i], View, options, elKey);
            }
        }
    },

    /**
     * Creates a single instance of a class using multiple elements.
     * @param {Array|HTMLCollection} els - The elements for which to setup an instance
     * @param {Function} View - The class to instantiate
     * @param {Object} [options] - The options to be passed to instantiation
     * @param {string} [elKey] - The key to use as the "el"
     * @private
     */
    _setupInstance: function (els, View, options, elKey) {
        elKey = elKey || 'el';
        var finalOptions = this._buildOptions(els, options);
        finalOptions[elKey] = els; // dont allow custom options to override the el!
        this._moduleCount++;
        var instance = this.subModules['fe' + this._moduleCount] = new View(finalOptions);
        this._formInstances.push(instance);
    },

    /**
     * Maps all supplied elements by an attribute.
     * @param {Array|HTMLCollection|NodeList} elements
     * @param {string} attr - The attribute to map by (the values will be the keys in the map returned)
     * @returns {Object} Returns the final object
     */
    mapElementsByAttribute: function (elements, attr) {
        var map = {},
            count = elements.length,
            i,
            el;
        if (count) {
            for (i = 0; i < count; i++) {
                el = elements[i];
                if (map[el[attr]]) {
                    map[el[attr]].push(el);
                } else {
                    map[el[attr]] = [el];
                }
            }
        }
        return map;
    },

    /**
     * Takes a set of elements and maps them by their name attributes.
     * @param {HTMLCollection|Array|NodeList} elements - An array of elements
     * @returns {{}} Returns an object with name/elements mapping
     * @deprecated since 2.0.0
     */
    mapElementsByName: function (elements) {
        return this.mapElementsByAttribute(elements, 'name');
    },

    /**
     * Returns the instance (if there is one) of an element with a specified name attribute
     * @param {string} name - The name attribute of the element whos instance is desired
     * @returns {Object} Returns the instance that matches the name specified
     */
    getInstanceByName: function (name) {
        var i,
            instance;

        for (i = 0; i < this._formInstances.length; i++) {
            instance = this._formInstances[i];
            if (instance.getFormElement().name === name) {
                break;
            }
        }
        return instance;
    },

    /**
     * Builds the initialize options for an element.
     * @param {HTMLElement|Array} el - The element (or if radio buttons, an array of elements)
     * @param {Object} options - The beginning set of options
     * @returns {*|{}}
     * @private
     */
    _buildOptions: function (el, options) {
        options = options || {};

        if (this.options.onGetOptions) {
            options = _.extend({}, options, this.options.onGetOptions(el));
        }
        options.onChange = function (value, inputEl, UIElement) {
            this._onValueChange(value, inputEl, UIElement);
        }.bind(this);
        return options;
    },

    /**
     * When any form element's value changes.
     * @param {string} value - The new value
     * @param {HTMLElement} el - The element that triggered value change
     * @param {HTMLElement} ui - The UI version of the element
     * @private
     */
    _onValueChange: function (value, el, ui) {

        if (this.options.onValueChange) {
            this.options.onValueChange(value, el, ui);
        }
        if (this.options.onChange) {
            this.options.onChange(value, el, ui);
        }

    },

    /**
     * Disables all form elements.
     */
    disable: function () {
        var els = this.formEls,
            i,
            submitButton = this.getSubmitButtonInstance();
        this.setPropertyAll('disabled', true);
        // add disabled css classes
        for (i = 0; i < els.length; i++) {
            els[i].kit.classList.add('disabled');
        }
        if (submitButton) {
            submitButton.disable();
        }

    },

    /**
     * Enables all form elements.
     */
    enable: function () {
        var els = this.formEls,
            i,
            submitButton = this.getSubmitButtonInstance();
        this.setPropertyAll('disabled', false);
        // remove disabled css classes
        for (i = 0; i < els.length; i++) {
            els[i].kit.classList.remove('disabled');
        }
        if (submitButton) {
            submitButton.disable();
        }
    },

    /**
     * Sets a property on all form elements.
     * @TODO: this function still exists until this class can cover ALL possible form elements (i.e. radio buttons)
     * @param {string} prop - The property to change
     * @param {*} value - The value to set
     */
    setPropertyAll: function (prop, value) {
        var i,
            els = this.formEls;
        for (i = 0; i < els.length; i++) {
            els[i][prop] = value;
        }
    },

    /**
     * Triggers a method on all form instances.
     * @param {string} method - The method
     * @param {...*} params - Any params for the method from here, onward
     */
    triggerMethodAll: function (method, params) {
        var args = Array.prototype.slice.call(arguments, 1),
            i, instance;

        for (i = 0; i < this._formInstances.length; i++) {
            instance = this._formInstances[i];
            instance[method].apply(instance, args);
        }
    },

    /**
     * Clears all form items.
     */
    clear: function () {
        this.triggerMethodAll('clear');
    },

    /**
     * Gets an object that maps all fields to their current name/value pairs.
     * @returns {Array} Returns an array of objects
     */
    getCurrentValues: function () {
        var map = [],
            fields = this.options.el.querySelectorAll('[name]'),
            fieldCount = fields.length,
            i,
            field,
            obj;
        for (i = 0; i < fieldCount; i++) {
            field = fields[i];
            // only add fields with a name attribute
            if (field.name) {
                obj = {
                    name: field.name,
                    // fallback to value attribute when .value can't be trusted (i.e. input[type=date])
                    value: field.value,
                    //value: field.value || field.getAttribute('value')
                    required: field.required,
                    disabled: field.disabled,
                    formElement: field
                };
                map.push(obj);
            }
        }
        return map;
    },

    /**
     * Returns the submit button instance.
     * @returns {Object}
     */
    getSubmitButtonInstance: function () {
        return this.subModules.submitButton;
    }

});

module.exports = Form;<commit_msg>Now supporting extensive list of HTML5 input types<commit_after>'use strict';
var _ = require('underscore');
var Module = require('module-js');
var Dropdown = require('./dropdown');
var InputField = require('./input-field');
var Checkbox = require('./checkbox');
var ButtonToggle = require('./button-toggle');
var SubmitButton = require('./submit-button');

require('element-kit');

/**
 * The function that fires when the input value changes
 * @callback Form~onValueChange
 * @param {string} value - The new value
 * @param {HTMLElement} element - The form element
 * @param {HTMLElement} uIElement - The ui-version of the form element
 */

/**
 * The function that fires to give users opportunity to return a custom set of options on a per-element basis
 * @callback Form~onGetOptions
 * @param {HTMLElement|Array} element(s) - The element (or if radio buttons, an array of elements) on which to use the custom options
 * @returns {Object} Return the custom set of options
 */

/**
 * The function that fires when the submit button is clicked
 * @callback Form~onSubmitButtonClick
 * @returns {Event} Returns the click event
 */

/**
 * Utility class for form elements.
 * @class Form
 */
var Form = Module.extend({

    /**
     * Sets up the form.
     * @param {object} options - The options
     * @param {HTMLFormElement} [options.el] - The form element
     * @param {HTMLFormElement} options.el - The form element
     * @param {Form~onValueChange} [options.onValueChange] - A callback function that fires when the value of any form element changes
     * @param {Function} [options.onGetOptions] - Function callback that is fired upon instantiation to provide custom options
     * @param {string} [options.dropdownClass] - The css class used to query the set of dropdown elements that should be included
     * @param {string} [options.checkboxClass] - The css class used to query the set of checkbox elements that should be included
     * @param {string} [options.inputFieldClass] - The css class used to query the set of text input elements that should be included
     * @param {string} [options.buttonToggleClass] - The css class used to query the set of radio button elements that should be included
     * @param {string} [options.submitButtonClass] - The css class used to query the submit button
     * @param {string} [options.submitButtonDisabledClass] - The class that will be applied to the submit button when its disabled
     * @param {string} [options.onSubmitButtonClick] - Function that is called when the submit button is clicked
     */
    initialize: function (options) {

        this.options = _.extend({
            el: null,
            onValueChange: null,
            onGetOptions: null,
            dropdownClass: null,
            checkboxClass: null,
            inputFieldClass: null,
            buttonToggleClass: null,
            submitButtonClass: null,
            submitButtonDisabledClass: null,
            onSubmitButtonClick: null
        }, options);

        // okay to cache here because its a "live" html collection -- yay!
        this.formEls = this.options.el.elements;

        this._formInstances = [];
        this._moduleCount = 0;
        Module.prototype.initialize.call(this, this.options);
    },

    /**
     * Returns a mapping of ids to their associated form option and selector.
     */
    _getSelectorMap: function () {
        return {
            dropdown: {
                option: this.options.dropdownClass,
                selector: 'select',
                tag: 'select'
            },
            checkbox: {
                option: this.options.checkboxClass,
                tag: 'input',
                types: ['checkbox']
            },
            input: {
                option: this.options.inputFieldClass,
                tag: 'input',
                types: [
                    'password', 'email', 'number', 'text', 'date',
                    'datetime', 'month', 'search', 'range', 'time',
                    'week', 'tel', 'color', 'datetime-local'
                ]
            },
            radio: {
                option: this.options.buttonToggleClass,
                tag: 'input',
                types: ['radio']
            }
        }
    },

    /**
     * Sets up the form and instantiates all necessary element classes.
     */
    setup: function () {
        var submitButtonEl = this.options.el.getElementsByClassName(this.options.submitButtonClass)[0];

        this._setupInstances(this._getInstanceEls('dropdown'), Dropdown);
        this._setupInstances(this._getInstanceEls('checkbox'), Checkbox);
        this._setupInstances(this._getInstanceEls('input'), InputField);

        // group radio button toggles by name before instantiating
        var radios = this._getInstanceEls('radio');
        _.each(this.mapElementsByAttribute(radios, 'name'), function (els) {
            this._setupInstance(els, ButtonToggle, {}, 'inputs');
        }, this);

        if (submitButtonEl) {
            this.subModules.submitButton = new SubmitButton({
                el: submitButtonEl,
                disabledClass: this.options.submitButtonDisabledClass,
                onClick: this.options.onSubmitButtonClick
            });
        }
    },

    /**
     * Gets the matching form elements, based on the supplied type.
     * @param {string} type - The type identifier (i.e. "dropdown", "checkbox", "input")
     * @returns {Array|HTMLCollection} Returns an array of matching elements
     * @private
     */
    _getInstanceEls: function (type) {
        var formEl = this.options.el,
            elements = [],
            map = this._getSelectorMap();

        map = map[type] || {};

        // we are strategically grabbing elements by "tagName" to ensure we have a LIVE HTMLCollection
        // instead of an ineffective, non-live NodeList (i.e. querySelector), can we say, "less state management"!

        if (map.option) {
            elements = formEl.getElementsByClassName(map.option);
        } else if (map.types) {
            map.types.forEach(function (val) {
                (this.mapElementsByAttribute(this.formEls, 'type')[val] || []).forEach(function (el) {
                    elements.push(el);
                });
            }, this);
        } else if (map.tag) {
            elements = formEl.getElementsByTagName(map.tag);
        }
        return elements;
    },

    /**
     * Creates a single instance of a class for each of the supplied elements.
     * @param {HTMLCollection|Array} elements - The set of elements to instance the class on
     * @param {Function} View - The class to instantiate
     * @param {Object} [options] - The options to be passed to instantiation
     * @param {string} [elKey] - The key to use as the "el"
     * @private
     */
    _setupInstances: function (elements, View, options, elKey) {
        var count = elements.length,
            i;
        if (count) {
            for (i = 0; i < count; i++) {
                this._setupInstance(elements[i], View, options, elKey);
            }
        }
    },

    /**
     * Creates a single instance of a class using multiple elements.
     * @param {Array|HTMLCollection} els - The elements for which to setup an instance
     * @param {Function} View - The class to instantiate
     * @param {Object} [options] - The options to be passed to instantiation
     * @param {string} [elKey] - The key to use as the "el"
     * @private
     */
    _setupInstance: function (els, View, options, elKey) {
        elKey = elKey || 'el';
        var finalOptions = this._buildOptions(els, options);
        finalOptions[elKey] = els; // dont allow custom options to override the el!
        this._moduleCount++;
        var instance = this.subModules['fe' + this._moduleCount] = new View(finalOptions);
        this._formInstances.push(instance);
    },

    /**
     * Maps all supplied elements by an attribute.
     * @param {Array|HTMLCollection|NodeList} elements
     * @param {string} attr - The attribute to map by (the values will be the keys in the map returned)
     * @returns {Object} Returns the final object
     */
    mapElementsByAttribute: function (elements, attr) {
        var map = {},
            count = elements.length,
            i,
            el;
        if (count) {
            for (i = 0; i < count; i++) {
                el = elements[i];
                if (map[el[attr]]) {
                    map[el[attr]].push(el);
                } else {
                    map[el[attr]] = [el];
                }
            }
        }
        return map;
    },

    /**
     * Takes a set of elements and maps them by their name attributes.
     * @param {HTMLCollection|Array|NodeList} elements - An array of elements
     * @returns {{}} Returns an object with name/elements mapping
     * @deprecated since 2.0.0
     */
    mapElementsByName: function (elements) {
        return this.mapElementsByAttribute(elements, 'name');
    },

    /**
     * Returns the instance (if there is one) of an element with a specified name attribute
     * @param {string} name - The name attribute of the element whos instance is desired
     * @returns {Object} Returns the instance that matches the name specified
     */
    getInstanceByName: function (name) {
        var i,
            instance;

        for (i = 0; i < this._formInstances.length; i++) {
            instance = this._formInstances[i];
            if (instance.getFormElement().name === name) {
                break;
            }
        }
        return instance;
    },

    /**
     * Builds the initialize options for an element.
     * @param {HTMLElement|Array} el - The element (or if radio buttons, an array of elements)
     * @param {Object} options - The beginning set of options
     * @returns {*|{}}
     * @private
     */
    _buildOptions: function (el, options) {
        options = options || {};

        if (this.options.onGetOptions) {
            options = _.extend({}, options, this.options.onGetOptions(el));
        }
        options.onChange = function (value, inputEl, UIElement) {
            this._onValueChange(value, inputEl, UIElement);
        }.bind(this);
        return options;
    },

    /**
     * When any form element's value changes.
     * @param {string} value - The new value
     * @param {HTMLElement} el - The element that triggered value change
     * @param {HTMLElement} ui - The UI version of the element
     * @private
     */
    _onValueChange: function (value, el, ui) {

        if (this.options.onValueChange) {
            this.options.onValueChange(value, el, ui);
        }
        if (this.options.onChange) {
            this.options.onChange(value, el, ui);
        }

    },

    /**
     * Disables all form elements.
     */
    disable: function () {
        var els = this.formEls,
            i,
            submitButton = this.getSubmitButtonInstance();
        this.setPropertyAll('disabled', true);
        // add disabled css classes
        for (i = 0; i < els.length; i++) {
            els[i].kit.classList.add('disabled');
        }
        if (submitButton) {
            submitButton.disable();
        }

    },

    /**
     * Enables all form elements.
     */
    enable: function () {
        var els = this.formEls,
            i,
            submitButton = this.getSubmitButtonInstance();
        this.setPropertyAll('disabled', false);
        // remove disabled css classes
        for (i = 0; i < els.length; i++) {
            els[i].kit.classList.remove('disabled');
        }
        if (submitButton) {
            submitButton.disable();
        }
    },

    /**
     * Sets a property on all form elements.
     * @TODO: this function still exists until this class can cover ALL possible form elements (i.e. radio buttons)
     * @param {string} prop - The property to change
     * @param {*} value - The value to set
     */
    setPropertyAll: function (prop, value) {
        var i,
            els = this.formEls;
        for (i = 0; i < els.length; i++) {
            els[i][prop] = value;
        }
    },

    /**
     * Triggers a method on all form instances.
     * @param {string} method - The method
     * @param {...*} params - Any params for the method from here, onward
     */
    triggerMethodAll: function (method, params) {
        var args = Array.prototype.slice.call(arguments, 1),
            i, instance;

        for (i = 0; i < this._formInstances.length; i++) {
            instance = this._formInstances[i];
            instance[method].apply(instance, args);
        }
    },

    /**
     * Clears all form items.
     */
    clear: function () {
        this.triggerMethodAll('clear');
    },

    /**
     * Gets an object that maps all fields to their current name/value pairs.
     * @returns {Array} Returns an array of objects
     */
    getCurrentValues: function () {
        var map = [],
            fields = this.options.el.querySelectorAll('[name]'),
            fieldCount = fields.length,
            i,
            field,
            obj;
        for (i = 0; i < fieldCount; i++) {
            field = fields[i];
            // only add fields with a name attribute
            if (field.name) {
                obj = {
                    name: field.name,
                    // fallback to value attribute when .value can't be trusted (i.e. input[type=date])
                    value: field.value,
                    //value: field.value || field.getAttribute('value')
                    required: field.required,
                    disabled: field.disabled,
                    formElement: field
                };
                map.push(obj);
            }
        }
        return map;
    },

    /**
     * Returns the submit button instance.
     * @returns {Object}
     */
    getSubmitButtonInstance: function () {
        return this.subModules.submitButton;
    }

});

module.exports = Form;<|endoftext|>


<commit_before>//     Zepto.js
//     (c) 2010-2014 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

;(function($){
  $.fn.serializeArray = function() {
    var el, name, type, result = [],
      add = function(value) {
        if (value.forEach) return value.forEach(add)
        result.push({ name: name, value: value })
      }
    if (this[0]) $.each(this[0].elements, function(){
      el = $(this)
      type = el.attr('type')
      name = this.name
      if (this.name && this.nodeName.toLowerCase() != 'fieldset' &&
        !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' &&
        ((type != 'radio' && type != 'checkbox') || this.checked))
          add(el.val())
    })
    return result
  }

  $.fn.serialize = function(){
    var result = []
    this.serializeArray().forEach(function(elm){
      result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value))
    })
    return result.join('&')
  }

  $.fn.submit = function(callback) {
    if (callback) this.bind('submit', callback)
    else if (this.length) {
      var event = $.Event('submit')
      this.eq(0).trigger(event)
      if (!event.isDefaultPrevented()) this.get(0).submit()
    }
    return this
  }

})(Zepto)
<commit_msg>Name the form field variable inside the loop<commit_after>//     Zepto.js
//     (c) 2010-2014 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

;(function($){
  $.fn.serializeArray = function() {
    var el, name, type, result = [],
      add = function(value) {
        if (value.forEach) return value.forEach(add)
        result.push({ name: name, value: value })
      }
    if (this[0]) $.each(this[0].elements, function(_, field){
      type = field.type, name = field.name
      if (name && field.nodeName.toLowerCase() != 'fieldset' &&
        !field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' &&
        ((type != 'radio' && type != 'checkbox') || field.checked))
          add($(field).val())
    })
    return result
  }

  $.fn.serialize = function(){
    var result = []
    this.serializeArray().forEach(function(elm){
      result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value))
    })
    return result.join('&')
  }

  $.fn.submit = function(callback) {
    if (callback) this.bind('submit', callback)
    else if (this.length) {
      var event = $.Event('submit')
      this.eq(0).trigger(event)
      if (!event.isDefaultPrevented()) this.get(0).submit()
    }
    return this
  }

})(Zepto)
<|endoftext|>