<commit_before>import dotenv from 'dotenv';
import express from 'express';
import bodyParser from 'body-parser';

import {
  Messenger,
  Button,
  Element,
  Image,
  Video,
  GenericTemplate,
  GreetingText,
  GetStartedButton,
  PersistentMenuItem,
  PersistentMenu,
  QuickReply,
  QuickReplies,
  ReceiptTemplate,
  Address,
  Summary,
  Adjustment,
} from '../lib/Messenger';

dotenv.config({ silent: true });

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const messenger = new Messenger({
  pageAccessToken: process.env.PAGE_ACCESS_TOKEN,
});

const WHITELISTED_DOMAINS = [
  'https://bbc.co.uk',
  'https://stackoverflow.com',
];

function init_bot() {
  messenger.addWhitelistedDomains(WHITELISTED_DOMAINS);

  // Greeting Text
  const greetingText = new GreetingText('Welcome to the bot demo.');
  messenger.setThreadSetting(greetingText)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });

  // Get Started Button
  const getStarted = new GetStartedButton('start');
  messenger.setThreadSetting(getStarted)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });

  // Persistent menu
  const menu_help = new PersistentMenuItem({
    type: 'postback',
    title: 'Help',
    payload: 'help',
  });

  const menu_docs = new PersistentMenuItem({
    type: 'web_url',
    title: 'Messenger Docs',
    url: 'https://developers.facebook.com/docs/messenger-platform',
  });

  const menu = new PersistentMenu([menu_help, menu_docs]);
  messenger.setThreadSetting(menu)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });
}

function get_button(ratio) {
  return new Button({
    type: 'web_url',
    title: 'Stack Overflow',
    url: 'https://stackoverflow.com',
    webview_height_ratio: ratio,
  });
}

function get_element(btn) {
  return new Element({
    title: 'Template example',
    item_url: 'https://stackoverflow.com',
    image_url: 'http://placehold.it/300x300',
    subtitle: 'Subtitle',
    buttons: [btn],
  });
}

messenger.on('message', (message) => {
  console.log(`Message received: ${JSON.stringify(message)}`);

  // Allow receiving locations
  if ('attachments' in message.message) {
    const msgType = message.message.attachments[0].type;
    if (msgType === 'location') {
      console.log('Location received');
      const text = `${message.message.attachments[0].title}:
                    lat: ${message.message.attachments[0].payload.coordinates.lat},
                    long: ${message.message.attachments[0].payload.coordinates.long}`;
      messenger.send({ text });
    }

    if (['audio', 'video', 'image', 'file'].includes(msgType)) {
      const attachment = message.message.attachments[0].payload.url;
      console.log(`Attachment received: ${attachment}`);
    }
  }

  // Text messages
  if ('text' in message.message) {
    let msg = message.message.text;
    msg = msg.toLowerCase();

    if (msg.includes('text')) {
      messenger.send({ text: 'This is an example text message.' });
    }

    if (msg.includes('image')) {
      messenger.send(new Image({
        url: 'https://unsplash.it/300/200/?random',
        is_reusable: true,
      }))
        .then((res) => {
          console.log(`Resuable attachment ID: ${res.attachment_id}`);
        });
    }

    if (msg.includes('reuse')) {
      messenger.send(new Image({ attachment_id: 947782652018100 }));
    }

    if (msg.includes('video')) {
      messenger.send(new Video({ url: 'http://techslides.com/demos/sample-videos/small.mp4' }));
    }

    if (msg.includes('payload')) {
      const pl = message.message.quick_reply.payload;
      const text = `User clicked button: ${msg}, Button payload is: ${pl}`;
      messenger.send({ text });
    }

    if (msg.includes('bubble')) {
      messenger.send(new GenericTemplate(
        [
          new Element({
            title: 'Example bubble',
            item_url: 'http://www.bbc.co.uk',
            image_url: 'https://unsplash.it/300/200/?random',
            subtitle: 'Opens bbc.co.uk',
            buttons: [
              new Button({
                type: 'web_url',
                title: 'BBC',
                url: 'http://www.bbc.co.uk',
              }),
            ],
          }),
        ]
      ));
    }

    if (msg.includes('quick replies')) {
      const qr1 = new QuickReply({ title: 'Location', content_type: 'location' });
      const qr2 = new QuickReply({ title: 'Payload', payload: 'QUICK_REPLY_PAYLOAD' });
      const qrs = new QuickReplies([qr1, qr2]);
      messenger.send(Object.assign(
        { text: 'This is an example with quick replies.' },
        qrs
      ));
    }

    if (msg.includes('compact')) {
      const btn = get_button('compact');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('tall')) {
      const btn = get_button('tall');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('full')) {
      const btn = get_button('full');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('receipt')) {
      const template = new ReceiptTemplate({
        recipient_name: 'Name',
        order_number: '123',
        currency: 'USD',
        payment_method: 'Visa',
        order_url: 'http://www.example.com',
        timestamp: '123123123',
        elements: [
          new Element({
            title: 'Title',
            subtitle: 'Subtitle',
            quantity: 1,
            price: 12.50,
            currency: 'USD',
          }),
        ],
        address: new Address({
          street_1: '1 Hacker Way',
          street_2: '',
          city: 'Menlo Park',
          postal_code: '94025',
          state: 'CA',
          country: 'US',
        }),
        summary: new Summary({
          subtotal: 75.00,
          shipping_cost: 4.95,
          total_tax: 6.19,
          total_cost: 56.14,
        }),
        adjustments: [
          new Adjustment({
            name: 'Adjustment',
            amount: 20,
          }),
        ],
      });
      messenger.send(template)
        .then((res) => {
          console.log(res);
        });
    }
  }
});

messenger.on('delivery', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('postback', (message) => {
  const payload = message.postback.payload;
  console.log(payload);
  if (payload === 'help') {
    console.log('Help payload');
    messenger.send({ text: 'A help message or template can go here.' })
      .then((res) => {
        console.log(res);
      });
  } else if (payload === 'start') {
    const text = 'Try sending me one of these messages: text, image, video, reuse, bubble, "quick replies", compact, tall, full';
    messenger.send({ text });
  }
});

messenger.on('read', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('optin', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('account_linking', () => {
  // console.log(messenger.lastEntry);
});

app.get('/webhook', (req, res) => {
  if (req.query['hub.mode'] === 'subscribe' &&
    req.query['hub.verify_token'] === process.env.VERIFY_TOKEN) {
    res.send(req.query['hub.challenge']);
  } else {
    res.sendStatus(400);
  }
});

app.get('/init', (req, res) => {
  init_bot();
  res.sendStatus(200);
});

app.post('/webhook', (req, res) => {
  res.sendStatus(200);
  messenger.handle(req.body);
});

app.listen(3000, () => {
  console.log('App listening on port 3000!');
});
<commit_msg>Moved intro text to constant<commit_after>import dotenv from 'dotenv';
import express from 'express';
import bodyParser from 'body-parser';

import {
  Messenger,
  Button,
  Element,
  Image,
  Video,
  GenericTemplate,
  GreetingText,
  GetStartedButton,
  PersistentMenuItem,
  PersistentMenu,
  QuickReply,
  QuickReplies,
  ReceiptTemplate,
  Address,
  Summary,
  Adjustment,
} from '../lib/Messenger';

dotenv.config({ silent: true });

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const messenger = new Messenger({
  pageAccessToken: process.env.PAGE_ACCESS_TOKEN,
});

const WHITELISTED_DOMAINS = [
  'https://bbc.co.uk',
  'https://stackoverflow.com',
];

const INTRO_MESSAGE = `Try sending me one of these messages:
text, image, video, reuse, bubble,
"quick replies", compact, tall, full`;

function init_bot() {
  messenger.addWhitelistedDomains(WHITELISTED_DOMAINS);

  // Greeting Text
  const greetingText = new GreetingText('Welcome to the bot demo.');
  messenger.setThreadSetting(greetingText)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });

  // Get Started Button
  const getStarted = new GetStartedButton('start');
  messenger.setThreadSetting(getStarted)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });

  // Persistent menu
  const menu_help = new PersistentMenuItem({
    type: 'postback',
    title: 'Help',
    payload: 'help',
  });

  const menu_docs = new PersistentMenuItem({
    type: 'web_url',
    title: 'Messenger Docs',
    url: 'https://developers.facebook.com/docs/messenger-platform',
  });

  const menu = new PersistentMenu([menu_help, menu_docs]);
  messenger.setThreadSetting(menu)
    .then((result) => {
      console.log(`Greeting Text: ${JSON.stringify(result)}`);
    });
}

function get_button(ratio) {
  return new Button({
    type: 'web_url',
    title: 'Stack Overflow',
    url: 'https://stackoverflow.com',
    webview_height_ratio: ratio,
  });
}

function get_element(btn) {
  return new Element({
    title: 'Template example',
    item_url: 'https://stackoverflow.com',
    image_url: 'http://placehold.it/300x300',
    subtitle: 'Subtitle',
    buttons: [btn],
  });
}

messenger.on('message', (message) => {
  console.log(`Message received: ${JSON.stringify(message)}`);

  // Allow receiving locations
  if ('attachments' in message.message) {
    const msgType = message.message.attachments[0].type;
    if (msgType === 'location') {
      console.log('Location received');
      const text = `${message.message.attachments[0].title}:
                    lat: ${message.message.attachments[0].payload.coordinates.lat},
                    long: ${message.message.attachments[0].payload.coordinates.long}`;
      messenger.send({ text });
    }

    if (['audio', 'video', 'image', 'file'].includes(msgType)) {
      const attachment = message.message.attachments[0].payload.url;
      console.log(`Attachment received: ${attachment}`);
    }
  }

  // Text messages
  if ('text' in message.message) {
    let msg = message.message.text;
    msg = msg.toLowerCase();

    if (msg.includes('text')) {
      messenger.send({ text: 'This is an example text message.' });
    }

    if (msg.includes('image')) {
      messenger.send(new Image({
        url: 'https://unsplash.it/300/200/?random',
        is_reusable: true,
      }))
        .then((res) => {
          console.log(`Resuable attachment ID: ${res.attachment_id}`);
        });
    }

    if (msg.includes('reuse')) {
      messenger.send(new Image({ attachment_id: 947782652018100 }));
    }

    if (msg.includes('video')) {
      messenger.send(new Video({ url: 'http://techslides.com/demos/sample-videos/small.mp4' }));
    }

    if (msg.includes('payload')) {
      const pl = message.message.quick_reply.payload;
      const text = `User clicked button: ${msg}, Button payload is: ${pl}`;
      messenger.send({ text });
    }

    if (msg.includes('bubble')) {
      messenger.send(new GenericTemplate(
        [
          new Element({
            title: 'Example bubble',
            item_url: 'http://www.bbc.co.uk',
            image_url: 'https://unsplash.it/300/200/?random',
            subtitle: 'Opens bbc.co.uk',
            buttons: [
              new Button({
                type: 'web_url',
                title: 'BBC',
                url: 'http://www.bbc.co.uk',
              }),
            ],
          }),
        ]
      ));
    }

    if (msg.includes('quick replies')) {
      const qr1 = new QuickReply({ title: 'Location', content_type: 'location' });
      const qr2 = new QuickReply({ title: 'Payload', payload: 'QUICK_REPLY_PAYLOAD' });
      const qrs = new QuickReplies([qr1, qr2]);
      messenger.send(Object.assign(
        { text: 'This is an example with quick replies.' },
        qrs
      ));
    }

    if (msg.includes('compact')) {
      const btn = get_button('compact');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('tall')) {
      const btn = get_button('tall');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('full')) {
      const btn = get_button('full');
      const elem = get_element(btn);
      messenger.send(new GenericTemplate([elem]));
    }

    if (msg.includes('receipt')) {
      const template = new ReceiptTemplate({
        recipient_name: 'Name',
        order_number: '123',
        currency: 'USD',
        payment_method: 'Visa',
        order_url: 'http://www.example.com',
        timestamp: '123123123',
        elements: [
          new Element({
            title: 'Title',
            subtitle: 'Subtitle',
            quantity: 1,
            price: 12.50,
            currency: 'USD',
          }),
        ],
        address: new Address({
          street_1: '1 Hacker Way',
          street_2: '',
          city: 'Menlo Park',
          postal_code: '94025',
          state: 'CA',
          country: 'US',
        }),
        summary: new Summary({
          subtotal: 75.00,
          shipping_cost: 4.95,
          total_tax: 6.19,
          total_cost: 56.14,
        }),
        adjustments: [
          new Adjustment({
            name: 'Adjustment',
            amount: 20,
          }),
        ],
      });
      messenger.send(template)
        .then((res) => {
          console.log(res);
        });
    }
  }
});

messenger.on('delivery', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('postback', (message) => {
  const payload = message.postback.payload;
  console.log(payload);
  if (payload === 'help') {
    console.log('Help payload');
    messenger.send({ text: 'A help message or template can go here.' })
      .then((res) => {
        console.log(res);
      });
  } else if (payload === 'start') {
    messenger.send({ INTRO_MESSAGE });
  }
});

messenger.on('read', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('optin', () => {
  // console.log(messenger.lastEntry);
});

messenger.on('account_linking', () => {
  // console.log(messenger.lastEntry);
});

app.get('/webhook', (req, res) => {
  if (req.query['hub.mode'] === 'subscribe' &&
    req.query['hub.verify_token'] === process.env.VERIFY_TOKEN) {
    res.send(req.query['hub.challenge']);
  } else {
    res.sendStatus(400);
  }
});

app.get('/init', (req, res) => {
  init_bot();
  res.sendStatus(200);
});

app.post('/webhook', (req, res) => {
  res.sendStatus(200);
  messenger.handle(req.body);
});

app.listen(3000, () => {
  console.log('App listening on port 3000!');
});
<|endoftext|>


<commit_before>/*jshint laxcomma:true, smarttabs: true, node:true */
'use strict';
/**
 * @module tastypie/lib/paginator
 * @author Eric Satterwhite
 * @requires util
 * @requires module:class
 * @requires module:class/options
 * @requires module:class/parent
 * @requires mout/lang/kindOf
 * @since 0.1.0
 * @requires xml2js
 * @requires util
 * @requires url
 * @requires events
 * @requires debug
 * @requires mout/lang/isNumber
 * @requires mout/lang/clone
 * @requires tastypie/lib/class
 * @requires tastypie/lib/class/options
 * @requires tastypie/lib/exceptions
 **/

var url        = require( 'url' )                  // url
	, Boom       = require('boom')
	, events     = require( 'events' )             // events
	, isNumber   = require( 'mout/lang/isNumber' ) // isNumber
	, clone      = require( 'mout/lang/clone' )    // clone
	, Class      = require( './class' )            // Class
	, Options    = require( './class/options' )    // Options
	, Parent     = require( './class/parent' )     // Parent function
	, Paginator
	;

/**
 * Used to generate pages of array data. Also responsible for creating Meta data packets for api responses
 * @constructor
 * @alias module:tastypie/lib/paginator
 * @param {Object} options
 * @param {Object[]} [options.object=[]] The data to page
 * @param {?Number} [options.limit=null] The maximum number of object to include in a page
 * @param {Number} [options.offset=0] The number of object to skip
 * @param {Request} options.req A hapi.js request object
 * @param {Reply} options.res A hapi.js reply object
 * @example var Paginator = require('tastypie/lib/paginator')
paginator = new Paginator({
	limit: bundle.req.query.limit || 25
, req: bundle.req
, res: bundle.res
, collectionName: 'data'
, objects: []
});

to_be_serialize = paginator.page();
 */
module.exports =
Paginator = new Class({
		inherits:events.EventEmitter
	, mixin:[ Parent, Options ]
	, options:{
		objects:[]
	  , limit:25
	  , max:1000
	  , offset:0
	  , req:null
	  , res:null
	  , collectionName: 'objects'
	}
	, constructor: function( options ){
		this.setOptions( options );
	}

	/**
	 * Generates a page of a data set
	 * @method module:tastypie/lib/paginator#page
	 * @return {Object} page An array sliced from the original data set using offset & limit parameters
	 **/
	, page: function page(){
			var limit = this.limit();
			var offset = this.offset();
			var count = this.count();
			var ret = {
				meta:{
					count:count
				, limit: limit
				, offset: offset
				}
			};

			if( limit  && limit < this.options.objects.length ){
				ret.meta.previous = this.previous( limit, offset, count );
				ret.meta.next = this.next( limit, offset, count );
			}

			ret[ this.options.collectionName ] = this.slice( limit, offset );
			// dereference the request and respons
			this.setOptions({ req:null, res:null });
			return ret;
	}

	/**
	 * Returns  the uri for the next page if their is one
	 * @method module:tastypie/lib/paginator#next
	 * @param {Number} limit the number of items in the page
	 * @param {Number} offset where in the data set to derive the page from
	 * @param {Number} count the length of the current data set
	 * @return {String} url the URI that can be used to retrieve the next page
	 **/
	, next: function next( limit, offset, count ){
		var value = limit + offset;

		if( value > count - 1 ){
			return null;
		}

		return this.uri(limit, value);
	}

	/**
	 * determines the offset for the current data set using options, or pased in request
	 * @method module:tastypie/lib/paginator#offset
	 * @return {Number} offset THe resulting offset parameter
	 **/
	, offset: function offset( ){
			var oset = this.options.offset;
			var query = this.options.req.query || {};

			if( query.offset != null ){
				oset = parseInt( query.offset, 10 );
			}

			if( !isNumber( oset ) || oset<0 ){
				var e = new Boom.badRequest("bad offset param: " + oset );
				e.req = this.options.req;
				e.res = this.options.res;
				return this.emit('error', e);
			}

			return oset;
	}

	/**
	 * Returns  the uri for the previous page if their is one
	 * @method module:tastypie/lib/paginator#previous
	 * @param {Number} limit the number of items in the page
	 * @param {Number} offset where in the data set to derive the page from
	 * @return {String} the URI that can be used to retrieve the previous page
	 **/
	, previous: function previous( limit, offset ){

		var value = offset - limit;
		if( value < 0 ){
			return null;
		}

		return this.uri( limit, value );
	}

	/**
	 * returns the number of objects in the current data set
	 * @method module:tastypie/lib/paginator#count
	 * @return {Number}
	 **/
	, count: function count(){
		return this.options.count;
	}

	/**
	 * retuns a slice from the current data set to represent a page
	 * @method module:tastypie/lib/paginator#slice
	 * @param {Number} limit The maximum number of items to include in the slice
	 * @param {Number} offset The starting position in the data set at which to start the slice
	 * @return {Array}
	 **/
	,slice: function slice( limit, offset ){
		if( limit <=0 ){
			return this.options.objects.slice( offset, this.options.objects.length );
		}

		return this.options.objects.slice( offset, ( offset ) + limit );
	}

	/**
	 * returns the number of items that should be included in the data set.
	 * Checks req.query for limit or a configured limit option
	 * @method module:tastypie/lib/paginator#limit
	 * @return {Number}
	 **/
	, limit: function limit(){
		var query = (this.options.req || {}).query || {}
		  , max   = this.options.max || -1
		  , limit = this.options.limit
		  , lmt
		  ;

		lmt = query.limit ? parseInt( query.limit, 10) : query.limit;
		lmt = isNumber( lmt ) ? lmt ? lmt : max : limit ? limit : 25;
		lmt = Math.min( lmt, max );
		if(!isNumber( lmt ) ){
			this.emit(
				'error'
				, new Boom.badRequest('limit must be a number')
				, this.options.req
				, this.options.res
			);
		}
		return lmt;
	}

	/**
	 * generates a URI that will return page of data
	 * @method module:tastypie/lib/paginator#uri
	 * @param {Number} limit The maximum number of items to include in the slice
	 * @param {Number} offset The starting position in the data set at which to start the slice
	 * @return {String} a formatted uri
	 **/
	, uri: function uri( limit, offset ){
		var values = clone(this.options.req.query || {});

		values.limit = limit;
		values.offset = offset;
		return url.format({
			 pathname:this.options.req.path
			 ,query: values
		});
	}
});

/**
 * Assumes paging is done at the data store level. Slice method returns the objects array untouched
 * @class module:tastypie/lib/paginator.Remote
 * @extends module:tastypie/lib/paginator
 * @param {Object} options
 * @param {Object[]} [options.object=[]] The data to page
 * @param {?Number} [options.limit=null] The maximum number of object to include in a page
 * @param {Number} [options.offset=0] The number of object to skip
 * @param {Request} options.req A hapi.js request object
 * @param {Reply} options.res A hapi.js reply object
 * @example var Paginator = require('tastypie/lib/paginator')
paginator = new Paginator.Remote({
	limit: bundle.req.query.limit || 25
, req: bundle.req
, res: bundle.res
, collectionName: 'data'
, objects: []
});

to_be_serialize = paginator.page();
 */
module.exports.Remote = new Class({
	inherits: Paginator
	,sort: function( ){
		return this.options.objects;
	}
	, slice: function( ){
		return this.options.objects;
	}
	/**
	 * Generates a page of a data set
	 * @method module:tastypie/lib/paginator.REMOTE#page
	 * @return {Object} page An array sliced from the original data set using offset & limit parameters
	 **/
	, page: function page(){
			var limit = this.limit()
			  , offset = this.offset()
			  , count = this.count()
			  , ret = {
					meta:{
						count:count
					  , limit: limit
					  , offset: offset
					}
			};

			if( limit  && limit < count ){
				ret.meta.previous = this.previous( limit, offset, count );
				ret.meta.next = this.next( limit, offset, count );
			}

			ret[ this.options.collectionName ] = this.slice( limit, offset );
			// dereference the request and respons
			this.setOptions({
				req:undefined
				, res:undefined
			});
			return ret;
	}
});

<commit_msg>fix bad usage of undef in paginator<commit_after>/*jshint laxcomma:true, smarttabs: true, node:true */
'use strict';
/**
 * @module tastypie/lib/paginator
 * @author Eric Satterwhite
 * @requires util
 * @requires module:class
 * @requires module:class/options
 * @requires module:class/parent
 * @requires mout/lang/kindOf
 * @since 0.1.0
 * @requires xml2js
 * @requires util
 * @requires url
 * @requires events
 * @requires debug
 * @requires mout/lang/isNumber
 * @requires mout/lang/clone
 * @requires tastypie/lib/class
 * @requires tastypie/lib/class/options
 * @requires tastypie/lib/exceptions
 **/

var url        = require( 'url' )                  // url
	, Boom       = require('boom')
	, events     = require( 'events' )             // events
	, isNumber   = require( 'mout/lang/isNumber' ) // isNumber
	, clone      = require( 'mout/lang/clone' )    // clone
	, Class      = require( './class' )            // Class
	, Options    = require( './class/options' )    // Options
	, Parent     = require( './class/parent' )     // Parent function
	, Paginator
	;

/**
 * Used to generate pages of array data. Also responsible for creating Meta data packets for api responses
 * @constructor
 * @alias module:tastypie/lib/paginator
 * @param {Object} options
 * @param {Object[]} [options.object=[]] The data to page
 * @param {?Number} [options.limit=null] The maximum number of object to include in a page
 * @param {Number} [options.offset=0] The number of object to skip
 * @param {Request} options.req A hapi.js request object
 * @param {Reply} options.res A hapi.js reply object
 * @example var Paginator = require('tastypie/lib/paginator')
paginator = new Paginator({
	limit: bundle.req.query.limit || 25
, req: bundle.req
, res: bundle.res
, collectionName: 'data'
, objects: []
});

to_be_serialize = paginator.page();
 */
module.exports =
Paginator = new Class({
		inherits:events.EventEmitter
	, mixin:[ Parent, Options ]
	, options:{
		objects:[]
	  , limit:25
	  , max:1000
	  , offset:0
	  , req:null
	  , res:null
	  , collectionName: 'objects'
	}
	, constructor: function( options ){
		this.setOptions( options );
	}

	/**
	 * Generates a page of a data set
	 * @method module:tastypie/lib/paginator#page
	 * @return {Object} page An array sliced from the original data set using offset & limit parameters
	 **/
	, page: function page(){
			var limit = this.limit();
			var offset = this.offset();
			var count = this.count();
			var ret = {
				meta:{
					count:count
				, limit: limit
				, offset: offset
				}
			};

			if( limit  && limit < this.options.objects.length ){
				ret.meta.previous = this.previous( limit, offset, count );
				ret.meta.next = this.next( limit, offset, count );
			}

			ret[ this.options.collectionName ] = this.slice( limit, offset );
			// dereference the request and respons
			this.setOptions({ req:null, res:null });
			return ret;
	}

	/**
	 * Returns  the uri for the next page if their is one
	 * @method module:tastypie/lib/paginator#next
	 * @param {Number} limit the number of items in the page
	 * @param {Number} offset where in the data set to derive the page from
	 * @param {Number} count the length of the current data set
	 * @return {String} url the URI that can be used to retrieve the next page
	 **/
	, next: function next( limit, offset, count ){
		var value = limit + offset;

		if( value > count - 1 ){
			return null;
		}

		return this.uri(limit, value);
	}

	/**
	 * determines the offset for the current data set using options, or pased in request
	 * @method module:tastypie/lib/paginator#offset
	 * @return {Number} offset THe resulting offset parameter
	 **/
	, offset: function offset( ){
			var oset = this.options.offset;
			var query = this.options.req.query || {};

			if( query.offset != null ){
				oset = parseInt( query.offset, 10 );
			}

			if( !isNumber( oset ) || oset<0 ){
				var e = new Boom.badRequest("bad offset param: " + oset );
				e.req = this.options.req;
				e.res = this.options.res;
				return this.emit('error', e);
			}

			return oset;
	}

	/**
	 * Returns  the uri for the previous page if their is one
	 * @method module:tastypie/lib/paginator#previous
	 * @param {Number} limit the number of items in the page
	 * @param {Number} offset where in the data set to derive the page from
	 * @return {String} the URI that can be used to retrieve the previous page
	 **/
	, previous: function previous( limit, offset ){

		var value = offset - limit;
		if( value < 0 ){
			return null;
		}

		return this.uri( limit, value );
	}

	/**
	 * returns the number of objects in the current data set
	 * @method module:tastypie/lib/paginator#count
	 * @return {Number}
	 **/
	, count: function count(){
		return this.options.count;
	}

	/**
	 * retuns a slice from the current data set to represent a page
	 * @method module:tastypie/lib/paginator#slice
	 * @param {Number} limit The maximum number of items to include in the slice
	 * @param {Number} offset The starting position in the data set at which to start the slice
	 * @return {Array}
	 **/
	,slice: function slice( limit, offset ){
		if( limit <=0 ){
			return this.options.objects.slice( offset, this.options.objects.length );
		}

		return this.options.objects.slice( offset, ( offset ) + limit );
	}

	/**
	 * returns the number of items that should be included in the data set.
	 * Checks req.query for limit or a configured limit option
	 * @method module:tastypie/lib/paginator#limit
	 * @return {Number}
	 **/
	, limit: function limit(){
		var query = (this.options.req || {}).query || {}
		  , max   = this.options.max || -1
		  , limit = this.options.limit
		  , lmt
		  ;

		lmt = query.limit ? parseInt( query.limit, 10) : query.limit;
		lmt = isNumber( lmt ) ? lmt ? lmt : max : limit ? limit : 25;
		lmt = Math.min( lmt, max );
		if(!isNumber( lmt ) ){
			this.emit(
				'error'
				, new Boom.badRequest('limit must be a number')
				, this.options.req
				, this.options.res
			);
		}
		return lmt;
	}

	/**
	 * generates a URI that will return page of data
	 * @method module:tastypie/lib/paginator#uri
	 * @param {Number} limit The maximum number of items to include in the slice
	 * @param {Number} offset The starting position in the data set at which to start the slice
	 * @return {String} a formatted uri
	 **/
	, uri: function uri( limit, offset ){
		var values = clone(this.options.req.query || {});

		values.limit = limit;
		values.offset = offset;
		return url.format({
			 pathname:this.options.req.path
			 ,query: values
		});
	}
});

/**
 * Assumes paging is done at the data store level. Slice method returns the objects array untouched
 * @class module:tastypie/lib/paginator.Remote
 * @extends module:tastypie/lib/paginator
 * @param {Object} options
 * @param {Object[]} [options.object=[]] The data to page
 * @param {?Number} [options.limit=null] The maximum number of object to include in a page
 * @param {Number} [options.offset=0] The number of object to skip
 * @param {Request} options.req A hapi.js request object
 * @param {Reply} options.res A hapi.js reply object
 * @example var Paginator = require('tastypie/lib/paginator')
paginator = new Paginator.Remote({
	limit: bundle.req.query.limit || 25
, req: bundle.req
, res: bundle.res
, collectionName: 'data'
, objects: []
});

to_be_serialize = paginator.page();
 */
module.exports.Remote = new Class({
	inherits: Paginator
	,sort: function( ){
		return this.options.objects;
	}
	, slice: function( ){
		return this.options.objects;
	}
	/**
	 * Generates a page of a data set
	 * @method module:tastypie/lib/paginator.REMOTE#page
	 * @return {Object} page An array sliced from the original data set using offset & limit parameters
	 **/
	, page: function page(){
			var limit = this.limit()
			  , offset = this.offset()
			  , count = this.count()
			  , ret = {
					meta:{
						count:count
					  , limit: limit
					  , offset: offset
					}
			};

			if( limit  && limit < count ){
				ret.meta.previous = this.previous( limit, offset, count );
				ret.meta.next = this.next( limit, offset, count );
			}

			ret[ this.options.collectionName ] = this.slice( limit, offset );
			// dereference the request and respons
			this.setOptions({ req:null, res:null });
			return ret;
	}
});

<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { selectMediaPickerQueryArgs, selectMedia } from '../../../actions/systemActions';
import { PICK_COLLECTION, PICK_SOURCE, PICK_COUNTRY, PICK_FEATURED } from '../../../lib/explorerUtil';
import SourceOrCollectionWidget from '../SourceOrCollectionWidget';
// import SelectedMediaContainer from './SelectedMediaContainer';

const localMessages = {
  pickCountry: { id: 'system.mediaPicker.select.pickCountry', defaultMessage: 'Search Geographic Collections' },
  pickCollections: { id: 'system.mediaPicker.select.pickCollections', defaultMessage: 'Search All Collections' },
  pickSources: { id: 'system.mediaPicker.select.pickSources', defaultMessage: 'Search Sources' },
  selectedMedia: { id: 'system.mediaPicker.selected.title', defaultMessage: 'Selected Media' },
  pickFeatured: { id: 'system.mediaPicker.select.pickFeatured', defaultMessage: 'Pick From Featured and Favorited' },
  pickStarred: { id: 'system.mediaPicker.select.pickStarred', defaultMessage: 'Pick From Starred' },
};

class PickedMediaContainer extends React.Component {

  updateMediaType = (type) => {
    const { updateMediaSelection } = this.props;
    updateMediaSelection(type);
  };
  render() {
    const { selectedMediaQueryType, selectedMedia, handleUnselectMedia } = this.props;
    const selectedMediaList =
      selectedMedia.map(obj => (
        <SourceOrCollectionWidget
          key={obj.id || obj.tags_id || obj.media_id}
          object={obj}
          onDelete={() => handleUnselectMedia(obj)}
        />
      ));
    const options = [
      { label: localMessages.pickFeatured, value: PICK_FEATURED },
      { label: localMessages.pickCountry, value: PICK_COUNTRY },
      { label: localMessages.pickCollections, value: PICK_COLLECTION },
      { label: localMessages.pickSources, value: PICK_SOURCE },
    ];
    return (

          {options.map((option, idx) => (
            <div
              key={idx}
              className={`select-media-option ${(selectedMediaQueryType === option.value) ? 'selected' : ''}`}
              onTouchTap={() => this.updateMediaType(option.value)}
            >

<FormattedMessage {...option.label} />

          ))}

<FormattedMessage {...localMessages.selectedMedia} />

          {selectedMediaList}

    );
  }
}

PickedMediaContainer.propTypes = {
  // from context
  intl: PropTypes.object.isRequired,
  // from parent
  selectedMedia: PropTypes.array,
  selectedMediaQueryType: PropTypes.number,
  updateMediaSelection: PropTypes.func.isRequired,
  handleUnselectMedia: PropTypes.func.isRequired,
};

const mapStateToProps = state => ({
  selectedMediaQueryType: state.system.mediaPicker.selectMediaQuery ? state.system.mediaPicker.selectMediaQuery.args.type : PICK_FEATURED,
  sourcesResults: state.system.mediaPicker.media ? state.system.mediaPicker.media.results : null, // resutl of query?
  collectionsResults: state.system.mediaPicker.collections ? state.system.mediaPicker.collections.results : null,
  favoritedCollections: state.system.mediaPicker.favoritedCollections ? state.system.mediaPicker.favoritedCollections.results : null,
  favoritedSources: state.system.mediaPicker.favoritedSources ? state.system.mediaPicker.favoritedSources.results : null,
});

const mapDispatchToProps = dispatch => ({
  updateMediaSelection: (type) => {
    if (type >= 0) {
      dispatch(selectMediaPickerQueryArgs({ type }));
    }
  },
  handleUnselectMedia: (selectedMedia) => {
    if (selectedMedia) {
      const unselectecMedia = Object.assign({}, selectedMedia, { selected: false });
      dispatch(selectMedia(unselectecMedia)); // disable button too
    }
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      PickedMediaContainer
    )
  );
<commit_msg>#904 UI title<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { selectMediaPickerQueryArgs, selectMedia } from '../../../actions/systemActions';
import { PICK_COLLECTION, PICK_SOURCE, PICK_COUNTRY, PICK_FEATURED } from '../../../lib/explorerUtil';
import SourceOrCollectionWidget from '../SourceOrCollectionWidget';
// import SelectedMediaContainer from './SelectedMediaContainer';

const localMessages = {
  pickCountry: { id: 'system.mediaPicker.select.pickCountry', defaultMessage: 'Search Geographic Collections' },
  pickCollections: { id: 'system.mediaPicker.select.pickCollections', defaultMessage: 'Search All Collections' },
  pickSources: { id: 'system.mediaPicker.select.pickSources', defaultMessage: 'Search Sources' },
  selectedMedia: { id: 'system.mediaPicker.selected.title', defaultMessage: 'Selected Media' },
  pickFeatured: { id: 'system.mediaPicker.select.pickFeatured', defaultMessage: 'Featured and Favorited' },
};

class PickedMediaContainer extends React.Component {

  updateMediaType = (type) => {
    const { updateMediaSelection } = this.props;
    updateMediaSelection(type);
  };
  render() {
    const { selectedMediaQueryType, selectedMedia, handleUnselectMedia } = this.props;
    const selectedMediaList =
      selectedMedia.map(obj => (
        <SourceOrCollectionWidget
          key={obj.id || obj.tags_id || obj.media_id}
          object={obj}
          onDelete={() => handleUnselectMedia(obj)}
        />
      ));
    const options = [
      { label: localMessages.pickFeatured, value: PICK_FEATURED },
      { label: localMessages.pickCountry, value: PICK_COUNTRY },
      { label: localMessages.pickCollections, value: PICK_COLLECTION },
      { label: localMessages.pickSources, value: PICK_SOURCE },
    ];
    return (

          {options.map((option, idx) => (
            <div
              key={idx}
              className={`select-media-option ${(selectedMediaQueryType === option.value) ? 'selected' : ''}`}
              onTouchTap={() => this.updateMediaType(option.value)}
            >

<FormattedMessage {...option.label} />

          ))}

<FormattedMessage {...localMessages.selectedMedia} />

          {selectedMediaList}

    );
  }
}

PickedMediaContainer.propTypes = {
  // from context
  intl: PropTypes.object.isRequired,
  // from parent
  selectedMedia: PropTypes.array,
  selectedMediaQueryType: PropTypes.number,
  updateMediaSelection: PropTypes.func.isRequired,
  handleUnselectMedia: PropTypes.func.isRequired,
};

const mapStateToProps = state => ({
  selectedMediaQueryType: state.system.mediaPicker.selectMediaQuery ? state.system.mediaPicker.selectMediaQuery.args.type : PICK_FEATURED,
  sourcesResults: state.system.mediaPicker.media ? state.system.mediaPicker.media.results : null, // resutl of query?
  collectionsResults: state.system.mediaPicker.collections ? state.system.mediaPicker.collections.results : null,
  favoritedCollections: state.system.mediaPicker.favoritedCollections ? state.system.mediaPicker.favoritedCollections.results : null,
  favoritedSources: state.system.mediaPicker.favoritedSources ? state.system.mediaPicker.favoritedSources.results : null,
});

const mapDispatchToProps = dispatch => ({
  updateMediaSelection: (type) => {
    if (type >= 0) {
      dispatch(selectMediaPickerQueryArgs({ type }));
    }
  },
  handleUnselectMedia: (selectedMedia) => {
    if (selectedMedia) {
      const unselectecMedia = Object.assign({}, selectedMedia, { selected: false });
      dispatch(selectMedia(unselectecMedia)); // disable button too
    }
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      PickedMediaContainer
    )
  );
<|endoftext|>


<commit_before>var program = require('commander');
var pkg = require('../../package.json');
var fs = require('fs');
var mkdirp = require('mkdirp');
var Helper = require('../helper');

program.version(pkg.version, '-v, --version');
program.option('');
program.option('    --home <path>' , 'home path');

require('./start');
require('./config');
require('./list');
require('./add');
require('./remove');
require('./reset');
require('./edit');

var argv = program.parseOptions(process.argv);
if (program.home) {
    Helper.HOME = program.home;
}

var config = Helper.HOME + '/config.js';
if (!fs.existsSync(config)) {
    mkdirp.sync(Helper.HOME);
    fs.writeFileSync(
        config,
        fs.readFileSync(__dirname + '/../../defaults/config.js')
    );
    console.log('Config created:');
    console.log(config);
}

program.parse(argv.args);

if (!program.args.length) {
    program.parse(process.argv.concat('start'));
}
<commit_msg>Lint pass: src/command-line/index.js<commit_after>/*eslint quotes: [2, "single"]*/
'use strict';

var path = require('path');
var program = require('commander');
var pkg = require('../../package.json');
var fs = require('fs');
var mkdirp = require('mkdirp');
var Helper = require('../helper');

program.version(pkg.version, '-v, --version');
program.option('');
program.option('    --home <path>', 'home path');

require('./start');
require('./config');
require('./list');
require('./add');
require('./remove');
require('./reset');
require('./edit');

var argv = program.parseOptions(process.argv);
if (program.home) {
    Helper.HOME = program.home;
}

var config = Helper.HOME + '/config.js';
if (!fs.existsSync(config)) {
    mkdirp.sync(Helper.HOME);
    fs.writeFileSync(
        config,
        fs.readFileSync(path.resolve(__dirname, '/../../defaults/config.js'))
    );
    console.log('Config created:');
    console.log(config);
}

program.parse(argv.args);

if (!program.args.length) {
    program.parse(process.argv.concat('start'));
}
<|endoftext|>


<commit_before>const { padEnd } = require('../../core/util')
const { Command } = require('../../core')

class HelpMenu extends Command {
  constructor (...args) {
    super(...args, {
      name: 'help',
      description: 'Displays info on commands',
      cooldown: 1,
      usage: [
        { name: 'command', type: 'command', optional: true }
      ]
    })
  }

  async handle ({ msg, commander, settings, args }, responder) {
    const prefix = settings.prefix
    if (args.command) {
      const command = args.command.cmd
      const name = command.labels[0]
      let desc = this.i18n.get(`descriptions.${name}`, settings.lang) || this.i18n.get(`${command.localeKey}.description`, settings.lang)
      if (typeof desc !== 'string') desc = command.description || '{{noDesc}}'
      let reply = [
        `**\`${prefix}${name}\`**  __\`${desc}\`__\n`,
        `**{{definitions.usage}}**: ${prefix}${command.labels[0]} ${Object.keys(command.resolver.usage).map(usage => {
          usage = command.resolver.usage[usage]
          return usage.optional ? `[${usage.displayName}]` : `<${usage.displayName}>`
        }).join(' ')}`
      ]
      if (command.labels.length > 1) {
        reply.push(`\n**{{definitions.aliases}}**: \`${command.labels.slice(1).join(' ')}\``)
      }
      reply.push('\n{{footer_group}}')
      responder.send(reply.join('\n'))
      return
    }
    let commands = {}
    let reply = [
      `{{header_1}} ${prefix === process.env.CLIENT_PREFIX ? '' : '{{header_1_alt}}'}`,
      '{{header_2}}',
      '{{header_3}}',
      '**```glsl'
    ]
    let maxPad = 10
    commander.unique().forEach(c => {
      if (c.cmd.labels[0] !== c.label || c.cmd.options.hidden || c.cmd.options.adminOnly) return
      const module = c.group
      const name = c.cmd.labels[0]
      let desc = this.i18n.get(`descriptions.${name}`, settings.lang) || this.i18n.get(`${c.cmd.localeKey}.description`, settings.lang)
      if (typeof desc !== 'string') desc = '{{noDesc}}'
      if (name.length > maxPad) maxPad = name.length
      if (!Array.isArray(commands[module])) commands[module] = []
      commands[module].push([name, desc])
    })
    for (let mod in commands) {
      if (commands[mod].length === 0) continue
      reply.push([
        `# ${mod}:`,
        commands[mod].map(c => `  ${padEnd(c[0], maxPad)} // ${c[1]}`).join('\n')
      ].join('\n'))
    }
    reply.push('```**', '{{footer}}')
    responder.send(reply.join('\n'), {
      DM: true,
      prefix: `\`${prefix}\``,
      defaultPrefix: `\`${process.env.CLIENT_PREFIX}\``,
      server: `**${msg.guild ? msg.guild.name : responder.t('{{pms}}')}**`,
      helpCommand: `\`${prefix}help <command>\``,
      exampleCommand: `\`${prefix}help credits\``,
      link: '**<https://discord.gg/bBqpAKw>**'
    })
    .then(m => {
      if (msg.guild) {
        responder.format('emoji:inbox').reply('{{checkPMs}}')
      }
    })
  }
}

module.exports = HelpMenu
<commit_msg>:sparkles: Refurnish help command<commit_after>const { padEnd } = require('../../core/util')
const { Command } = require('../../core')

class HelpMenu extends Command {
  constructor (...args) {
    super(...args, {
      name: 'help',
      description: 'Displays info on commands',
      cooldown: 1,
      usage: [
        { name: 'command', type: 'command', optional: true }
      ]
    })
  }

  async handle ({ msg, commander, settings, args }, responder) {
    const prefix = settings.prefix
    if (args.command) {
      const command = args.command.cmd
      const name = command.labels[0]
      let desc = this.i18n.get(`descriptions.${name}`, settings.lang) || this.i18n.get(`${command.localeKey}.description`, settings.lang)
      if (typeof desc !== 'string') desc = command.description || '{{noDesc}}'
      let reply = [
        `**\`${prefix}${name}\`**  __\`${desc}\`__\n`,
        `**{{definitions.usage}}**: ${prefix}${command.labels[0]} ${Object.keys(command.resolver.usage).map(usage => {
          usage = command.resolver.usage[usage]
          return usage.optional ? `[${usage.displayName}]` : `<${usage.displayName}>`
        }).join(' ')}`
      ]
      if (command.labels.length > 1) {
        reply.push(`\n**{{definitions.aliases}}**: \`${command.labels.slice(1).join(' ')}\``)
      }
      reply.push('\n{{footer_group}}')
      responder.send(reply.join('\n'))
      return
    }

    let maxPad = 10
    const commands = commander.unique().reduce((obj, c) => {
      if (c.cmd.labels[0] !== c.label || c.cmd.options.hidden || c.cmd.options.adminOnly) return obj
      const module = c.group
      const name = c.cmd.labels[0]

      let desc = this.i18n.get(`descriptions.${name}`, settings.lang) ||
      this.i18n.get(`${c.cmd.localeKey}.description`, settings.lang)
      if (typeof desc !== 'string') desc = '{{noDesc}}'

      if (name.length > maxPad) maxPad = name.length
      if (!Array.isArray(obj[module])) obj[module] = []
      obj[module].push([name, desc])
      return obj
    }, {})

    let toSend = []
    for (const mod in commands) {
      if (!commands[mod].length) continue
      toSend.push([
        `# ${mod}:`,
        commands[mod].map(c => `  ${padEnd(c[0], maxPad)} // ${c[1]}`).join('\n')
      ].join('\n'))
      if (toSend.length >= 10) {
        await responder.send(['**```glsl'].concat(toSend, '```**'), { DM: true })
        toSend = []
      }
    }
    if (toSend.length) await responder.send(['**```glsl'].concat(toSend, '```**'), { DM: true })

    return responder.send([
      `{{header_1}} ${prefix === process.env.CLIENT_PREFIX ? '' : '{{header_1_alt}}'}`,
      '{{header_2}}',
      '{{header_3}}',
      '{{footer}}'
    ], {
      DM: true,
      prefix: `\`${prefix}\``,
      defaultPrefix: `\`${process.env.CLIENT_PREFIX}\``,
      server: `**${msg.guild ? msg.guild.name : responder.t('{{pms}}')}**`,
      helpCommand: `\`${prefix}help <command>\``,
      exampleCommand: `\`${prefix}help credits\``,
      link: '**<https://discord.gg/bBqpAKw>**'
    })
    .then(m => {
      if (msg.guild) {
        responder.format('emoji:inbox').reply('{{checkPMs}}')
      }
    })
  }
}

module.exports = HelpMenu
<|endoftext|>


<commit_before>// sample code for BLE power meter relay module
var _ = require('busyman');

var options = {
        baudRate: 115200,
        rtscts: true,
        flowControl: true
    };

var BShepherd = require('ble-shepherd'),
	central = new BShepherd('noble');   // use 'noble' when a BLE USB adaptor is used
    // central = new BShepherd('cc-bnp', '/dev/ttyACM0', options); // use 'cc-bnp' when cc2540 USB dongle is used.

var relayPlugin = require('bshep-plugin-sivann-relay'); // place the bshep-plugin-sivann-xxx.js file at the same directory with the xxxxSampleCode.js

central.support('relay', relayPlugin);

central.start();

/************************/
/* Event handle         */
/************************/
/*** central is ready ***/
central.on('ready', function () {
    console.log('[         ready ] ');
    bleApp(central);
});

/*** permitJoining    ***/
central.on('permitJoining', function (timeLeft) {
    console.log('[ permitJoining ] ' + timeLeft + ' sec');
});

/*** error            ***/
central.on('error', function (err) {
    console.log('[         error ] ' + err.message);
});

/**********************************/
/* BLE Application                */
/**********************************/
var relay, relay1, relay2;
function bleApp (central) {
	var blocker = central.blocker;

	/*** add your devices to blacklist ***/
	//blocker.enable('black');         // enable blacklist service. Use blacklist to ban a known devices.
    //blocker.block('0x5c313e2bfb34'); // ban a specified device by its MAC address

	/*** add your devices to whitelist ***/
    //blocker.enable('white');         // enable whitelist service. Use whitelist to block other unknown/unwanted BLE devices, and only specified devices can join your network.
	//blocker.unblock('0x20c38ff19428');  // specify a device to join the network by using its MAC address
	//blocker.unblock('0x689e192a8e2d');

    central.permitJoin(60);             // 60s the default value to allow devices joining the network.
    central.on('ind', function(msg) {
		var dev = msg.periph;

		switch (msg.type) {
            /*** devIncoming      ***/
			case 'devIncoming':
                console.log('[   devIncoming ] ' + '@' + dev.addr + ', ' + dev.name ); // display the device MAC and name. Use this MAC address for blacklist or whitelist.

				if(dev.name === 'relay') {
					relay = dev;
                    /***  write your application here   ***/

                    // you can call the private function to enable all the indication/notification of each Characteristic automatically.
                    configNotifyAll(relay);
                    // you can also manually enable or disable the indication/notification of each Characteristic.
                    // relay.configNotify('0xbb40', '0xcc0e', true); // Relay
                    // relay.configNotify('0xbb30', '0xcc1e', true); // Power
                    // relay.configNotify('0xbb30', '0xcc13', true); // Current
                    // relay.configNotify('0xbb90', '0xcc06', true); // PIR
                    relay.configNotify('0xbb00', '0xcc00', false); // DIn
                    relay.configNotify('0xbb10', '0xcc02', false); // AIn

                    // Register your handler to handle notification or indication of each Characteristic.
                    relay.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                    relay.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                    relay.onNotified('0xbb30', '0xcc13', callbackCurrent);  // Current
                    relay.onNotified('0xbb90', '0xcc06', callbackPir);      // PIR
                    relay.onNotified('0xbb00', '0xcc00', callbackDIn);      // DIn
                    relay.onNotified('0xbb10', '0xcc02', callbackAIn);      // AIn
                    relay.write('0xbb30', '0xbb32', {period: 250}, function (err) {
                        if (err)
                            console.log('[         error ] ' + err);
                        else
                            console.log('[ debug message ] changed the reporting period to 2.5s.'); // (recommend range: 100-255)
                    });
                    //relay.write('0xbb30', '0xbb31', {config : false});    // uncomment to turn off power & current measurements.

                    /*** you will have to switch case between device addresses only if you have multiple relay modules. ***/
/*                    switch (dev.addr) {
                        case '0x20c38ff19428':
                            //  write your application for the 1st relay  //
                            relay1 = dev;
                            configNotifyAll(relay1);
                            relay1.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                            relay1.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                            relay1.onNotified('0xbb90', '0xcc06', callbackPir1);      // PIR
                            relay1.write('0xbb30', '0xbb32', {period: 255}, function (err) {
                                if (err)
                                    console.log('[         error ] ' + err);
                                else
                                    console.log('[ debug message ] changed the reporting period to 2.55s.'); // (recommand range: 100-255)
                            });
                            break;
                        case '0x689e192a8e2d':
                            //  write your application for the 2nd relay  //
							relay2 = dev;
                            configNotifyAll(relay2);
                            relay2.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                            relay2.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                            relay2.onNotified('0xbb90', '0xcc06', callbackPir2);      // PIR
                            relay2.write('0xbb30', '0xbb32', {period: 255}, function (err) {
                                if (err)
                                    console.log('[         error ] ' + err);
                                else
                                    console.log('[ debug message ] changed the reporting period to 2.55s.'); // (recommand range: 100-255)
                            });
							break;
                    }
 */
				}
				break;

            /***   devStatus     ***/
            case 'devStatus':
                console.log('[     devStatus ] ' + '@' + dev.addr + ', ' + msg.data);
                break;

            /***   devLeaving    ***/
            case 'devLeaving':
				console.log('[    devLeaving ]' + '@' + dev.addr);
				break;

            /***   attrsChange   ***/
			case 'attChange':
                //console.log('[   attrsChange ] ' + '@' + dev.addr + ', ' + dev.name + ', ' + msg.data.sid.uuid + ', ' + msg.data.cid.uuid + ', ' + JSON.stringify(msg.data.value));  // print all attribute changes once received.
				break;
            /***   attNotify     ***/
			case 'attNotify':
				break;

            /***   devNeedPasskey   ***/
			case 'devNeedPasskey':
				// cc-bnp only
				console.log('[devNeedPasskey ]');
				break;
		}
    });
}

/*****************************************************/
/*    Power Meter Relay Callback Handler             */
/*****************************************************/
function callbackRelay(data) {
    // show relay state
    console.log('[ debug message ] Relay state: ' + data.onOff);
    /***  write your application here   ***/
}

function callbackPower(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackCurrent(data) {
    // show current
    console.log('[ debug message ] Current sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackMeterConfig(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackMeterPeriod(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackPir(data) {
    var pwrCtrlChar;

    // show pir state
    console.log('[ debug message ] PIR State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay) {
        // if PIR is triggered and relay is present, then switch relay to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay to NO');
        });
    } else if (!data.dInState && relay) {
        // if PIR is not triggered and relay is present, then switch relay to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay to NC');
        });

    }
}

function callbackPir1(data) {
    var pwrCtrlChar;

    // show pir1 state
    console.log('[ debug message ] PIR1 State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay1) {
        // if PIR1 is triggered and relay1 is present, then switch relay1 to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay1.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay1 to NO');
        });
    } else if (!data.dInState && relay1) {
        // if PIR1 is not triggered and relay1 is present, then switch relay1 to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay1.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay1 to NC');
        });

    }
}

function callbackPir2(data) {
    var pwrCtrlChar;

    // show pir2 state
    if(data.dInState)
        console.log('[ debug message ] PIR2 State: ' + data.dInState);
    else
        console.log('[ debug message ] PIR2 State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay2) {
        // if PIR2 is triggered and relay2 is present, then switch relay2 to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay2.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay2 to NO');
        });
    } else if (!data.dInState && relay2) {
        // if PIR2 is not triggered and relay2 is present, then switch relay2 to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay2.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay2 to NC');
        });

    }
}

function callbackDIn(data) {
    // show dIn
    console.log('[ debug message ] dIn State : ' + data.dInState);
    /***  write your application here   ***/
}

function callbackAIn(data) {
    // show aIn
    console.log('[ debug message ] aIn sensed value: ' + data.aInCurrValue + ' ' + data.sensorType);
    /***  write your application here   ***/
}

/**********************************/
/* Private Utility Function       */
/**********************************/
function configNotifyAll(dev) {
	var devData = {
		permAddr: dev.addr,
		status: dev.status,
		gads: {}
	};

	_.forEach(dev.dump().servList, function (serv) {
		_.forEach(serv.charList, function (char) {

			if (!_.isNil(devData)) {
				devData.gads[devData.auxId] = devData;
				if (dev._controller)
					dev.configNotify(serv.uuid, char.handle, true);
			}
		});
	});

	return devData;
}<commit_msg>Add chalk<commit_after>// sample code for BLE power meter relay module
var _ = require('busyman'),
    chalk = require('chalk');

var options = {
        baudRate: 115200,
        rtscts: true,
        flowControl: true
    };

var BShepherd = require('ble-shepherd'),
	central = new BShepherd('noble');   // use 'noble' when a BLE USB adaptor is used
    // central = new BShepherd('cc-bnp', '/dev/ttyACM0', options); // use 'cc-bnp' when cc2540 USB dongle is used.

var relayPlugin = require('bshep-plugin-sivann-relay'); // place the bshep-plugin-sivann-xxx.js file at the same directory with the xxxxSampleCode.js

central.support('relay', relayPlugin);

central.start();

/************************/
/* Event handle         */
/************************/
/*** central is ready ***/
central.on('ready', function () {
    console.log(chalk.green('[         ready ] '));
    bleApp(central);
});

/*** permitJoining    ***/
central.on('permitJoining', function (timeLeft) {
    console.log(chalk.green('[ permitJoining ] ') + timeLeft + ' sec');
});

/*** error            ***/
central.on('error', function (err) {
    console.log(chalk.red('[         error ] ') + err.message);
});

/**********************************/
/* BLE Application                */
/**********************************/
var relay, relay1, relay2;
function bleApp (central) {
	var blocker = central.blocker;

	/*** add your devices to blacklist ***/
	//blocker.enable('black');         // enable blacklist service. Use blacklist to ban a known devices.
    //blocker.block('0x5c313e2bfb34'); // ban a specified device by its MAC address

	/*** add your devices to whitelist ***/
    //blocker.enable('white');         // enable whitelist service. Use whitelist to block other unknown/unwanted BLE devices, and only specified devices can join your network.
	//blocker.unblock('0x20c38ff19428');  // specify a device to join the network by using its MAC address
	//blocker.unblock('0x689e192a8e2d');

    central.permitJoin(60);             // 60s the default value to allow devices joining the network.
    central.on('ind', function(msg) {
		var dev = msg.periph;

		switch (msg.type) {
            /*** devIncoming      ***/
			case 'devIncoming':
                console.log(chalk.yellow('[   devIncoming ] ') + '@' + dev.addr + ', ' + dev.name ); // display the device MAC and name. Use this MAC address for blacklist or whitelist.

				if(dev.name === 'relay') {
					relay = dev;
                    /***  write your application here   ***/

                    // you can call the private function to enable all the indication/notification of each Characteristic automatically.
                    configNotifyAll(relay);
                    // you can also manually enable or disable the indication/notification of each Characteristic.
                    // relay.configNotify('0xbb40', '0xcc0e', true); // Relay
                    // relay.configNotify('0xbb30', '0xcc1e', true); // Power
                    // relay.configNotify('0xbb30', '0xcc13', true); // Current
                    // relay.configNotify('0xbb90', '0xcc06', true); // PIR
                    relay.configNotify('0xbb00', '0xcc00', false); // DIn
                    relay.configNotify('0xbb10', '0xcc02', false); // AIn

                    // Register your handler to handle notification or indication of each Characteristic.
                    relay.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                    relay.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                    relay.onNotified('0xbb30', '0xcc13', callbackCurrent);  // Current
                    relay.onNotified('0xbb90', '0xcc06', callbackPir);      // PIR
                    relay.onNotified('0xbb00', '0xcc00', callbackDIn);      // DIn
                    relay.onNotified('0xbb10', '0xcc02', callbackAIn);      // AIn
                    relay.write('0xbb30', '0xbb32', {period: 250}, function (err) {
                        if (err)
                            console.log('[         error ] ' + err);
                        else
                            console.log('[ debug message ] changed the reporting period to 2.5s.'); // (recommend range: 100-255)
                    });
                    //relay.write('0xbb30', '0xbb31', {config : false});    // uncomment to turn off power & current measurements.

                    /*** you will have to switch case between device addresses only if you have multiple relay modules. ***/
/*                    switch (dev.addr) {
                        case '0x20c38ff19428':
                            //  write your application for the 1st relay  //
                            relay1 = dev;
                            configNotifyAll(relay1);
                            relay1.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                            relay1.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                            relay1.onNotified('0xbb90', '0xcc06', callbackPir1);      // PIR
                            relay1.write('0xbb30', '0xbb32', {period: 255}, function (err) {
                                if (err)
                                    console.log('[         error ] ' + err);
                                else
                                    console.log('[ debug message ] changed the reporting period to 2.55s.'); // (recommand range: 100-255)
                            });
                            break;
                        case '0x689e192a8e2d':
                            //  write your application for the 2nd relay  //
							relay2 = dev;
                            configNotifyAll(relay2);
                            relay2.onNotified('0xbb40', '0xcc0e', callbackRelay);    // Relay
                            relay2.onNotified('0xbb30', '0xcc1e', callbackPower);    // Power
                            relay2.onNotified('0xbb90', '0xcc06', callbackPir2);      // PIR
                            relay2.write('0xbb30', '0xbb32', {period: 255}, function (err) {
                                if (err)
                                    console.log('[         error ] ' + err);
                                else
                                    console.log('[ debug message ] changed the reporting period to 2.55s.'); // (recommand range: 100-255)
                            });
							break;
                    }
 */
				}
				break;

            /***   devStatus     ***/
            case 'devStatus':
                var status
                if (msg.data === 'online')
                    status = chalk.green(msg.data);
                else
                    status = chalk.red(msg.data);
                console.log(chalk.magenta('[     devStatus ] ') + '@' + dev.addr + ', ' + status);
                break;

            /***   devLeaving    ***/
            case 'devLeaving':
				console.log(chalk.yellow('[    devLeaving ]') + '@' + dev.addr);
				break;

            /***   attrsChange   ***/
			case 'attChange':
                //console.log(chalk.blue('[   attrsChange ] ') + '@' + dev.addr + ', ' + dev.name + ', ' + msg.data.sid.uuid + ', ' + msg.data.cid.uuid + ', ' + JSON.stringify(msg.data.value));  // print all attribute changes once received.
				break;
            /***   attNotify     ***/
			case 'attNotify':
				break;

            /***   devNeedPasskey   ***/
			case 'devNeedPasskey':
				// cc-bnp only
				console.log('[devNeedPasskey ]');
				break;
		}
    });
}

/*****************************************************/
/*    Power Meter Relay Callback Handler             */
/*****************************************************/
function callbackRelay(data) {
    // show relay state
    console.log('[ debug message ] Relay state: ' + data.onOff);
    /***  write your application here   ***/
}

function callbackPower(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackCurrent(data) {
    // show current
    console.log('[ debug message ] Current sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackMeterConfig(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackMeterPeriod(data) {
    // show power
    console.log('[ debug message ] Power sensed value: ' + data.sensorValue + ' ' + data.units);
    /***  write your application here   ***/
}

function callbackPir(data) {
    var pwrCtrlChar;

    // show pir state
    console.log('[ debug message ] PIR State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay) {
        // if PIR is triggered and relay is present, then switch relay to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay to NO');
        });
    } else if (!data.dInState && relay) {
        // if PIR is not triggered and relay is present, then switch relay to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay to NC');
        });

    }
}

function callbackPir1(data) {
    var pwrCtrlChar;

    // show pir1 state
    console.log('[ debug message ] PIR1 State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay1) {
        // if PIR1 is triggered and relay1 is present, then switch relay1 to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay1.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay1 to NO');
        });
    } else if (!data.dInState && relay1) {
        // if PIR1 is not triggered and relay1 is present, then switch relay1 to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay1.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay1 to NC');
        });

    }
}

function callbackPir2(data) {
    var pwrCtrlChar;

    // show pir2 state
    if(data.dInState)
        console.log('[ debug message ] PIR2 State: ' + data.dInState);
    else
        console.log('[ debug message ] PIR2 State: ' + data.dInState);

    /*** Example: interaction between PIR state and Relay switch   ***/
    if (data.dInState && relay2) {
        // if PIR2 is triggered and relay2 is present, then switch relay2 to NO
        // relay service : 0xbb40, char: 0xcc0e
        relay2.write('0xbb40', '0xcc0e', {onOff: 1}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay2 to NO');
        });
    } else if (!data.dInState && relay2) {
        // if PIR2 is not triggered and relay2 is present, then switch relay2 to NC
        // relay service : 0xbb40, char: 0xcc0e
        relay2.write('0xbb40', '0xcc0e', {onOff: 0}, function (err) {
            if (err)
                console.log('[         error ] ' + err);
            else
                console.log('[ debug message ] switch relay2 to NC');
        });

    }
}

function callbackDIn(data) {
    // show dIn
    console.log('[ debug message ] dIn State : ' + data.dInState);
    /***  write your application here   ***/
}

function callbackAIn(data) {
    // show aIn
    console.log('[ debug message ] aIn sensed value: ' + data.aInCurrValue + ' ' + data.sensorType);
    /***  write your application here   ***/
}

/**********************************/
/* Private Utility Function       */
/**********************************/
function configNotifyAll(dev) {
	var devData = {
		permAddr: dev.addr,
		status: dev.status,
		gads: {}
	};

	_.forEach(dev.dump().servList, function (serv) {
		_.forEach(serv.charList, function (char) {

			if (!_.isNil(devData)) {
				devData.gads[devData.auxId] = devData;
				if (dev._controller)
					dev.configNotify(serv.uuid, char.handle, true);
			}
		});
	});

	return devData;
}<|endoftext|>


<commit_before>(function() {
    "use strict";

    var token = null;
    var authCallbacks = [];
    var authEndpoint = "https://trello.com";

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

    function setUpApi() {
        var windowLocation = window.location;
        window.Trello = {
            deauthorize: function() {
                token = null;
                delete localStorage.trelloToken;
            },
            authorize: function(appKey, opts) {
                opts = jQuery.extend(true, {
                    scope: {
                        read: true,
                        write: false,
                        account: false
                    },
                    expiration: "30days"
                }, opts);
                if (token == null) {
                  token = localStorage.trelloToken;
                }
                if (token != null) {
                    if (isFunction(opts.success)) {
                        opts.success(token);
                    }
                    return;
                }

                authCallbacks.push(opts);

                var scopeArray = Object.keys(opts.scope).filter(function (k) {
                    return opts.scope[k];
                });
                var leftCoordinate = window.screenX + (window.innerWidth - 420) / 2;
                var topCoordinate = window.screenY + (window.innerHeight - 470) / 2;
                var temp;
                var returnUrl = (temp = /^[a-z]+:\/\/[^\/]*/.exec(windowLocation)) != null ?
                    temp[0] : undefined;
                var authUrl = authEndpoint + "/1/authorize?" +
                    jQuery.param({
                        "return_url": returnUrl,
                        "callback_method": "postMessage",
                        scope: scopeArray.join(","),
                        expiration: opts.expiration,
                        name: opts.name,
                        "response_type": "token",
                        key: appKey
                    });
                window.open(authUrl, "trello", "width=420,height=470,left=" +
                  leftCoordinate + ",top=" + topCoordinate);
            }
        };

        function handleMessage(event) {
            if (event.origin === authEndpoint) {
                if (event.source != null) {
                    event.source.close();
                }
                token = event.data != null && event.data.length > 4 ? event.data : null;
                if (token != null) {
                    localStorage.trelloToken = token;
                }
                var authCallback;
                while (authCallbacks.length > 0) {
                    authCallback = authCallbacks.shift();
                    if (token != null) {
                        if (isFunction(authCallback.success)) {
                            authCallback.success(token);
                        }
                    } else {
                        if (isFunction(authCallback.error)) {
                            authCallback.error();
                        }
                    }
                }
            }
        }
        window.addEventListener("message", handleMessage, false);
    }

    setUpApi();
})();
<commit_msg>Simplify Trello logic further<commit_after>"use strict";

var token = null;
var authCallbacks = [];
var authEndpoint = "https://trello.com";
var windowLocation = window.location;

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

window.Trello = {
    deauthorize: function() {
        token = null;
        delete localStorage.trelloToken;
    },
    authorize: function(appKey, opts) {
        opts = jQuery.extend(true, {
            scope: {
                read: true,
                write: false,
                account: false
            },
            expiration: "30days"
        }, opts);
        if (token == null) {
          token = localStorage.trelloToken;
        }
        if (token != null) {
            if (isFunction(opts.success)) {
                opts.success(token);
            }
            return;
        }

        authCallbacks.push(opts);

        var scopeArray = Object.keys(opts.scope).filter(function (k) {
            return opts.scope[k];
        });
        var leftCoordinate = window.screenX + (window.innerWidth - 420) / 2;
        var topCoordinate = window.screenY + (window.innerHeight - 470) / 2;
        var temp;
        var returnUrl = (temp = /^[a-z]+:\/\/[^\/]*/.exec(windowLocation)) != null ?
            temp[0] : undefined;
        var authUrl = authEndpoint + "/1/authorize?" +
            jQuery.param({
                "return_url": returnUrl,
                "callback_method": "postMessage",
                scope: scopeArray.join(","),
                expiration: opts.expiration,
                name: opts.name,
                "response_type": "token",
                key: appKey
            });
        window.open(authUrl, "trello", "width=420,height=470,left=" +
          leftCoordinate + ",top=" + topCoordinate);
    }
};

function handleMessage(event) {
    if (event.origin === authEndpoint) {
        if (event.source != null) {
            event.source.close();
        }
        token = event.data != null && event.data.length > 4 ? event.data : null;
        if (token != null) {
            localStorage.trelloToken = token;
        }
        var authCallback;
        while (authCallbacks.length > 0) {
            authCallback = authCallbacks.shift();
            if (token != null) {
                if (isFunction(authCallback.success)) {
                    authCallback.success(token);
                }
            } else {
                if (isFunction(authCallback.error)) {
                    authCallback.error();
                }
            }
        }
    }
}
window.addEventListener("message", handleMessage, false);
<|endoftext|>


<commit_before>// Constructeur de la classe ProcessingImage
function ProcessingImage(src) {

    this.img = new Image();
    this.img.src = src;

    var myImage = this;

    this.img.onload = function() {
        myImage.w = this.width;
        myImage.h = this.height;

        canvas = document.getElementById('canvas');

        myImage.initialx = canvas.width/2 - this.width/2;
        myImage.initialy = canvas.height/2 - this.height/2;

        myImage.x = myImage.initialx;
        myImage.y = myImage.initialy;
    }
}

//Dessine l'image
ProcessingImage.prototype.draw = function(ctx) {
    var locx = this.x;
    var locy = this.y;
    ctx.drawImage(this.img,locx,locy);
}

// Determine si un point est dans l'image
ProcessingImage.prototype.contains = function(mx, my) {

  return  (this.x <= mx) && (this.x + (this.w) >= mx) &&
          (this.y <= my) && (this.y + (this.h) >= my);
}

//Constructeur de la baseline
function Baseline(lines) {
    this.lines = lines;
    this.visible = false;

}

Baseline.prototype.draw = function(ctx, image){
    if(this.visible)
    {
        for (index = 0; index < this.lines.length; index++) {
            ctx.beginPath();
            ctx.moveTo(image.x,image.y + this.lines[index]);
            ctx.lineTo(image.x + image.w,image.y + this.lines[index]);
            ctx.strokeStyle = '#2ecc71';
            ctx.stroke();
        }
    }

}

function Rectangle(rect){
    this.rect = rect;
    this.color = '#3498db';
    this.selectedColor = '#e74c3c';
    this.visible = true;
    this.selected = false;
}

Rectangle.prototype.draw = function(ctx, image){
    if(this.visible)
    {
        if(this.selected)
            ctx.fillStyle = this.selectedColor;
        else
            ctx.fillStyle = this.color;

        ctx.globalAlpha=0.2;
        ctx.fillRect(image.x + this.rect.x,image.y + this.rect.y, this.rect.w, this.rect.h);

    }
}

//Constructeur de la bounding box
function BoundingBox(rects) {
    this.rects = rects;

}

BoundingBox.prototype.draw = function(ctx, image){
    for (index = 0; index < this.rects.length; index++) {
        this.rects[index].draw(ctx, image);
    }
}
BoundingBox.prototype.contains = function(mx, my, image){
    for (index = 0; index < this.rects.length; index++) {
        if((this.rects[index].rect.x + image.x <= mx)
            && (this.rects[index].rect.x + image.x + this.rects[index].rect.w >=mx)
            && (this.rects[index].rect.y + image.y <= my)
            && (this.rects[index].rect.y + image.y + this.rects[index].rect.h >= my))
            return index;
    }
    return 'false';
}
BoundingBox.prototype.select = function (id){
    for (index = 0; index < this.rects.length; index++) {
        this.rects[index].selected = false;
    }
    if(id != 'false')
        this.rects[id].selected = true;
}

function CanvasState(canvas, image, baseline, boundingBox, previewCanvas) {

    var myState = this;

    this.canvas = canvas;
    this.width = canvas.width;
    this.height = canvas.height;
    this.ctx = canvas.getContext('2d');

    this.image = image;

    this.baseline = baseline;
    this.boundingBox = boundingBox;

    this.previewCanvas = previewCanvas;

    this.dragging = false;

    this.scale = 1.0;

    this.panX = 0;
    this.panY = 0;

    //Coordonnées de la selection sur l'image
    this.dragoffx = 0;
    this.dragoffy = 0;

    canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);

    canvas.addEventListener('click', function(e) {

        var mouse = myState.getMouse(e);
        var mx = mouse.x;
        var my = mouse.y;

        var id = myState.boundingBox.contains(mx, my, myState.image);
        if(id != 'false')
        {
            myState.boundingBox.select(id);
            myState.previewCanvas.zoomTo(myState.boundingBox.rects[id]);
            myState.previewCanvas.visible = true;
            $(".container_right").show();
        }
        else
        {
            myState.boundingBox.select('false');
            $(".container_right").hide();
        }

    }, true);
    canvas.addEventListener('mousedown', function(e) {

        var mouse = myState.getMouse(e);
        var mx = mouse.x;
        var my = mouse.y;

        var image = myState.image;

        if (image.contains(mx, my)) {

            myState.dragoffx = mx - image.x;
            myState.dragoffy = my - image.y;
            myState.dragging = true;
            return;
          }
    }, true);

    canvas.addEventListener('mousemove', function(e) {
        if (myState.dragging){
            var mouse = myState.getMouse(e);
            image.x = (mouse.x) - myState.dragoffx;
            image.y = (mouse.y) - myState.dragoffy;
        }
    }, true);

    canvas.addEventListener('mouseup', function(e) {
        myState.dragging = false;
    }, true);

    document.getElementById('zoom-in').addEventListener('click', function(e){
        myState.scale += 0.1;
    }, true);

    document.getElementById('zoom-out').addEventListener('click', function(e){
        myState.scale -= 0.1;
    }, true);

    document.getElementById('zoom-reset').addEventListener('click', function(e){
        myState.scale = 1;
        myState.image.x = myState.image.initialx;
        myState.image.y = myState.image.initialy;
    }, true);

    document.getElementById('baseline').addEventListener('change', function(e){
        if(this.checked){
            myState.baseline.visible = true;
        }
        else{
            myState.baseline.visible = false;
        }
    }, true);

    document.getElementById('valid-reset').addEventListener('click', function(e){

        var data = new FormData();
        data.append('file', myState.image.img.src);

        $.ajax({
            url: 'remove.php?restart',
            type: 'POST',
            data: data,
            cache: false,
            processData: false, // Don't process the files
            contentType: false, // Set content type to false as jQuery will tell the server its a query string request
            success: function(data, textStatus, jqXHR)
            {
                var parsedData = JSON.parse(data);
                if (parsedData == true) {
                    location.reload();
                    console.log("Page réinitialisée.");
                }
                else
                {
                    console.log('ERRORS: ' + data.error);
                }
            },
            error: function(jqXHR, textStatus, errorThrown)
            {
                console.log('ERRORS: ' + textStatus);
            }
          });

    }, true);

    this.interval = 30;
    setInterval(function() { myState.draw(); }, myState.interval);

}

// Efface le canvas pour redessiner
CanvasState.prototype.clear = function() {
  this.ctx.clearRect(0, 0, this.width, this.height);
}

// Dessine le canvas avec ces éléments
CanvasState.prototype.draw = function() {
  if (!this.valid) {
    var ctx = this.ctx;

    var newWidth = this.width * this.scale;
    var newHeight = this.height * this.scale;
    this.panX = -((newWidth-this.width)/2);
    this.panY = -((newHeight-this.height)/2);

    this.clear();

    ctx.save();

    ctx.translate(this.panX, this.panY);

    ctx.scale(this.scale, this.scale);

    this.image.draw(ctx);

    this.baseline.draw(ctx, this.image);

    this.boundingBox.draw(ctx, this.image);

    this.previewCanvas.draw();

    ctx.restore();

  }
}

// Retourne les coordonnées de la souris
CanvasState.prototype.getMouse = function(e) {
    var rect = this.canvas.getBoundingClientRect();
    var mx = Math.floor((e.clientX-rect.left)/(rect.right-rect.left)*this.canvas.width);
    var my = Math.floor((e.clientY-rect.top)/(rect.bottom-rect.top)*this.canvas.height);

    var mouseXT = parseInt((mx - this.panX) / this.scale);
    var mouseYT = parseInt((my - this.panY) / this.scale);

    return {x: mouseXT, y: mouseYT};
}

function PreviewCanvas(canvas, image)
{
    this.canvas = canvas;
    this.width = canvas.width;
    this.height = canvas.height;
    this.ctx = canvas.getContext('2d');

    this.image = image;

    this.visible = false;
    this.scale = 9;

    var myPreviewCanvas = this;

}

// Efface le canvas pour redessiner
PreviewCanvas.prototype.clear = function() {
    this.ctx.clearRect(0, 0, this.width, this.height);
}
// Dessine le canvas avec ces éléments
PreviewCanvas.prototype.draw = function() {
    if(this.visible)
    {
        var ctx = this.ctx;
        this.clear();
        ctx.save();

        var newWidth = this.width * this.scale;
        var newHeight = this.height * this.scale;
        var panX = -((newWidth-this.width)/2);
        var panY = -((newHeight-this.height)/2);

        ctx.translate(panX, panY);

        ctx.scale(this.scale,this.scale);
        this.image.draw(ctx);
        ctx.restore();
    }

}
PreviewCanvas.prototype.zoomTo = function(rect){

    this.image.x = (this.width/2) - rect.rect.x - rect.rect.w/2;
    this.image.y = (this.height/2) - rect.rect.y - rect.rect.h/2;
}

function init(src) {
    var image = new ProcessingImage(src);
    var imagePreview = new ProcessingImage(src);
    var baseline = new Baseline([33, 94, 155]);
    var rect = new Rectangle({x:14, y:20, w:8, h: 14});
    var rect2 = new Rectangle({x:23, y:20, w:8, h: 14});
    var rect3 = new Rectangle({x:32, y:20, w:8, h: 14});
    var boundingBox = new BoundingBox([rect, rect2, rect3]);

    var previewCanvas = new PreviewCanvas(document.getElementById('small_canvas'), imagePreview);
    var normalCanvas = new CanvasState(document.getElementById('canvas'), image, baseline, boundingBox, previewCanvas);

}

<commit_msg>Resolution bugs + Modification Baseline accepte debut et fin<commit_after>// Constructeur de la classe ProcessingImage
function ProcessingImage(src) {

    this.img = new Image();
    this.img.src = src;

}

//Dessine l'image
ProcessingImage.prototype.draw = function(ctx) {
    var locx = this.x;
    var locy = this.y;
    ctx.drawImage(this.img,locx,locy);
}

// Determine si un point est dans l'image
ProcessingImage.prototype.contains = function(mx, my) {

  return  (this.x <= mx) && (this.x + (this.w) >= mx) &&
          (this.y <= my) && (this.y + (this.h) >= my);
}

//Constructeur de la baseline
function Baseline(lines) {
    this.lines = lines;
    this.visible = false;

}

Baseline.prototype.draw = function(ctx, image){
    if(this.visible)
    {
        for (index = 0; index < this.lines.length; index++) {
            ctx.beginPath();
            ctx.moveTo(image.x+ this.lines[index].xstart,image.y + this.lines[index].y);
            ctx.lineTo(image.x + this.lines[index].xfinish,image.y + this.lines[index].y);
            ctx.strokeStyle = '#2ecc71';
            ctx.stroke();
        }
    }

}

function Rectangle(rect){
    this.rect = rect;
    this.color = '#3498db';
    this.selectedColor = '#e74c3c';
    this.visible = true;
    this.selected = false;
}

Rectangle.prototype.draw = function(ctx, image){
    if(this.visible)
    {
        if(this.selected)
            ctx.fillStyle = this.selectedColor;
        else
            ctx.fillStyle = this.color;

        ctx.globalAlpha=0.2;
        ctx.fillRect(image.x + this.rect.x,image.y + this.rect.y, this.rect.w, this.rect.h);

    }
}

//Constructeur de la bounding box
function BoundingBox(rects) {
    this.rects = rects;

}

BoundingBox.prototype.draw = function(ctx, image){
    for (index = 0; index < this.rects.length; index++) {
        this.rects[index].draw(ctx, image);
    }
}
BoundingBox.prototype.contains = function(mx, my, image){
    for (index = 0; index < this.rects.length; index++) {
        if((this.rects[index].rect.x + image.x <= mx)
            && (this.rects[index].rect.x + image.x + this.rects[index].rect.w >=mx)
            && (this.rects[index].rect.y + image.y <= my)
            && (this.rects[index].rect.y + image.y + this.rects[index].rect.h >= my))
            return index;
    }
    return 'false';
}
BoundingBox.prototype.select = function (id){
    for (index = 0; index < this.rects.length; index++) {
        this.rects[index].selected = false;
    }
    if(id != 'false')
        this.rects[id].selected = true;
}

function CanvasState(canvas, image, baseline, boundingBox, previewCanvas) {

    var myState = this;

    this.canvas = canvas;
    this.width = canvas.width;
    this.height = canvas.height;
    this.ctx = canvas.getContext('2d');

    this.image = image;

    this.baseline = baseline;
    this.boundingBox = boundingBox;

    this.previewCanvas = previewCanvas;

    this.dragging = false;

    this.scale = 1.0;

    this.panX = 0;
    this.panY = 0;

    //Coordonnées de la selection sur l'image
    this.dragoffx = 0;
    this.dragoffy = 0;

    this.image.img.onload = function(){
        myState.image.w = this.width;
        myState.image.h = this.height;

        myState.image.initialx = myState.width/2 - this.width/2;
        myState.image.initialy = myState.height/2 - this.height/2;

        myState.image.x = myState.image.initialx;
        myState.image.y = myState.image.initialy;

        if(this.width > this.height)
            myState.scale = myState.width /this.width;
        else
            myState.scale = myState.height /this.height;
    }

    canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);

    canvas.addEventListener('click', function(e) {

        var mouse = myState.getMouse(e);
        var mx = mouse.x;
        var my = mouse.y;

        var id = myState.boundingBox.contains(mx, my, myState.image);
        if(id != 'false')
        {
            myState.boundingBox.select(id);
            myState.previewCanvas.zoomTo(myState.boundingBox.rects[id]);
            myState.previewCanvas.visible = true;
            $(".container_right").show();
        }
        else
        {
            myState.boundingBox.select('false');
            $(".container_right").hide();
        }

    }, true);
    canvas.addEventListener('mousedown', function(e) {

        var mouse = myState.getMouse(e);
        var mx = mouse.x;
        var my = mouse.y;

        var image = myState.image;

        if (image.contains(mx, my)) {

            myState.dragoffx = mx - image.x;
            myState.dragoffy = my - image.y;
            myState.dragging = true;
            return;
          }
    }, true);

    canvas.addEventListener('mousemove', function(e) {
        if (myState.dragging){
            var mouse = myState.getMouse(e);
            image.x = (mouse.x) - myState.dragoffx;
            image.y = (mouse.y) - myState.dragoffy;
        }
    }, true);

    canvas.addEventListener('mouseup', function(e) {
        myState.dragging = false;
    }, true);

    document.getElementById('zoom-in').addEventListener('click', function(e){
        myState.scale += 0.1;
    }, true);

    document.getElementById('zoom-out').addEventListener('click', function(e){
        myState.scale -= 0.1;
    }, true);

    document.getElementById('zoom-reset').addEventListener('click', function(e){
        if(this.width > this.height)
            myState.scale = myState.width /myState.image.w;
        else
            myState.scale = myState.height /myState.image.h;

        myState.image.x = myState.image.initialx;
        myState.image.y = myState.image.initialy;
    }, true);

    document.getElementById('baseline').addEventListener('change', function(e){
        if(this.checked){
            myState.baseline.visible = true;
        }
        else{
            myState.baseline.visible = false;
        }
    }, true);

    document.getElementById('valid-reset').addEventListener('click', function(e){

        var data = new FormData();
        data.append('file', myState.image.img.src);

        $.ajax({
            url: 'remove.php?restart',
            type: 'POST',
            data: data,
            cache: false,
            processData: false, // Don't process the files
            contentType: false, // Set content type to false as jQuery will tell the server its a query string request
            success: function(data, textStatus, jqXHR)
            {
                var parsedData = JSON.parse(data);
                if (parsedData == true) {
                    location.reload();
                    console.log("Page réinitialisée.");
                }
                else
                {
                    console.log('ERRORS: ' + data.error);
                }
            },
            error: function(jqXHR, textStatus, errorThrown)
            {
                console.log('ERRORS: ' + textStatus);
            }
          });

    }, true);

    this.interval = 30;
    setInterval(function() { myState.draw(); }, myState.interval);

}

// Efface le canvas pour redessiner
CanvasState.prototype.clear = function() {
  this.ctx.clearRect(0, 0, this.width, this.height);
}

// Dessine le canvas avec ces éléments
CanvasState.prototype.draw = function() {
  if (!this.valid) {
    var ctx = this.ctx;

    var newWidth = this.width * this.scale;
    var newHeight = this.height * this.scale;
    this.panX = -((newWidth-this.width)/2);
    this.panY = -((newHeight-this.height)/2);

    this.clear();

    ctx.save();

    ctx.translate(this.panX, this.panY);

    ctx.scale(this.scale, this.scale);

    this.image.draw(ctx);

    this.baseline.draw(ctx, this.image);

    this.boundingBox.draw(ctx, this.image);

    this.previewCanvas.draw();

    ctx.restore();

  }
}

// Retourne les coordonnées de la souris
CanvasState.prototype.getMouse = function(e) {
    var rect = this.canvas.getBoundingClientRect();
    var mx = Math.floor((e.clientX-rect.left)/(rect.right-rect.left)*this.canvas.width);
    var my = Math.floor((e.clientY-rect.top)/(rect.bottom-rect.top)*this.canvas.height);

    var mouseXT = parseInt((mx - this.panX) / this.scale);
    var mouseYT = parseInt((my - this.panY) / this.scale);

    return {x: mouseXT, y: mouseYT};
}

function PreviewCanvas(canvas, image)
{
    this.canvas = canvas;
    this.width = canvas.width;
    this.height = canvas.height;
    this.ctx = canvas.getContext('2d');

    this.image = image;

    this.visible = false;
    this.scale = 9;

    var myPreviewCanvas = this;

}

// Efface le canvas pour redessiner
PreviewCanvas.prototype.clear = function() {
    this.ctx.clearRect(0, 0, this.width, this.height);
}
// Dessine le canvas avec ces éléments
PreviewCanvas.prototype.draw = function() {
    if(this.visible)
    {
        var ctx = this.ctx;
        this.clear();
        ctx.save();

        var newWidth = this.width * this.scale;
        var newHeight = this.height * this.scale;
        var panX = -((newWidth-this.width)/2);
        var panY = -((newHeight-this.height)/2);

        ctx.translate(panX, panY);

        ctx.scale(this.scale,this.scale);
        this.image.draw(ctx);
        ctx.restore();
    }

}
PreviewCanvas.prototype.zoomTo = function(rect){

    this.image.x = (this.width/2) - rect.rect.x - rect.rect.w/2;
    this.image.y = (this.height/2) - rect.rect.y - rect.rect.h/2;
    if(rect.rect.w > rect.rect.h)
        this.scale = this.width /rect.rect.w;
    else
        this.scale = this.height /rect.rect.h;
}

function init(src) {
    var image = new ProcessingImage(src);
    var imagePreview = new ProcessingImage(src);
    var baseline = new Baseline([{xstart:50, y:90, xfinish:560}, {xstart:50, y:130, xfinish:570}]);
    var box1 = new Rectangle({x:50, y:60, w:30, h: 30});
    var box2 = new Rectangle({x:82, y:70, w:18, h: 20});
    var box3 = new Rectangle({x:102, y:68, w:16, h: 22});
    var boundingBox = new BoundingBox([box1, box2, box3]);

    var previewCanvas = new PreviewCanvas(document.getElementById('small_canvas'), imagePreview);
    var normalCanvas = new CanvasState(document.getElementById('canvas'), image, baseline, boundingBox, previewCanvas);

}

<|endoftext|>