<commit_before>var dgram = require('dgram');
var Buffer = require('buffer').Buffer;
var nodeConsole = console;

var DefaultHostname = require("os").hostname();
var DefaultAddress = "127.0.0.1";
var SingletonInstance = null;

 var socket
   , socketUsers = 0
   , releaseTimeout
   , socketErrorHandler = function (err) {
         if (err) {
             nodeConsole.error('socket error: ' + err)
         } else {
             nodeConsole.error('unknown socket error!')
         }

         if (socket !== undefined) {
             socket.close()
             socket = undefined
             socketUsers = 0
         }
     }
   , getSocket = function () {
         if (undefined === socket) {
             socket = dgram.createSocket('udp4')
             socket.on('error', socketErrorHandler)
         }
         ++socketUsers
         //console.log(socketUsers)
         return socket
     }
   , releaseSocket = function () {
         --socketUsers
         //console.log(socketUsers)
         if (0 == socketUsers && undefined === releaseTimeout) {
             releaseTimeout = setTimeout(function () {
                 if (0 == socketUsers && socket !== undefined) {
                     //console.log('closing socket!')
                     socket.close()
                     socket = undefined
                 } /*else {
                     console.log('another user attached while waiting to close the socket')
                 }*/
                 releaseTimeout = undefined
             }, 1000)
         }
     }

var Transport = {
    UDP: function(message, severity) {
        var self = this;
        var syslogMessage = this.composerFunction(message, severity);

        getSocket().send(syslogMessage,
                    0,
                    syslogMessage.length,
                    this.port,
                    this.address,
                    function(err, bytes) {
                      self._logError(err, bytes);
                      releaseSocket();
                    }
        );
    },

    file: (function() {
        var logTarget ;

        switch(require('os').type()) {
            case 'Darwin':
                logTarget = '/var/run/syslog' ;
                break ;

            case 'Linux':
                logTarget = '/dev/log' ;
                break ;

            default:
                throw new Error('Unknown OS Type: ' + require('os').type()) ;

        }

        return function(message, severity) {
            var client = dgram.createSocket('unix_dgram') ;
            var syslogMessage = this.composerFunction(message, severity);
            client.send(syslogMessage,
                        0,
                        syslogMessage.length,
                        logTarget,
                        this._logError
            );
            client.close() ;

        };
    })()
};

var Facility = {
    kern:   0,
    user:   1,
    mail:   2,
    daemon: 3,
    auth:   4,
    syslog: 5,
    lpr:    6,
    news:   7,
    uucp:   8,
    local0: 16,
    local1: 17,
    local2: 18,
    local3: 19,
    local4: 20,
    local5: 21,
    local6: 22,
    local7: 23
};

var Severity = {
    emerg:  0,
    alert:  1,
    crit:   2,
    err:    3,
    warn:   4,
    notice: 5,
    info:   6,
    debug:  7
};

// Format RegExp
var formatRegExp = /%[sdj]/g;

/**
 * Just copy from node.js console
 * @param f
 * @returns
 */
function format(f) {
  var   util = require('util'),
        i    = 0;

  if (typeof f !== 'string') {
    var objects = [];
    for (i = 0; i < arguments.length; i++) {
      objects.push(util.inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  i = 1;
  var args = arguments;
  var str = String(f).replace(formatRegExp, function(x) {
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j': return JSON.stringify(args[i++]);
      default:
        return x;
    }
  });
  for (var len = args.length, x = args[i]; i < len; x = args[++i]) {
    if (x === null || typeof x !== 'object') {
      str += ' ' + x;
    } else {
      str += ' ' + util.inspect(x);
    }
  }
  return str;
}

/**
 * Syslog logger
 * @constructor
 * @returns {SysLogger}
 */
function SysLogger(config) {
    this._times = {};
    this._logError = function(err, other) {
      if(err){
        nodeConsole.error('Cannot log message via %s:%d', this.hostname, this.port);
      }
    }.bind(this);
    this.set(config);
    return this;
}

/**
* Get singleton instance of SysLogger.
* @returns {SysLogger}
*/
SysLogger.getInstance = function() {
    if(!SingletonInstance){
        SingletonInstance = new SysLogger();
    }
    return SingletonInstance;
};

/**
 * Init function, takes a configuration object. If a hostname is provided the transport is assumed
 * to be Transport.UDP
 * @param {Object} configuration object with the following keys:
 *          - tag       - {String}                  By default is __filename
 *          - facility  - {Facility|Number|String}  By default is "user"
 *          - hostname  - {String}                  By default is require("os").hostname()
 *          - port      - {Number}                  Defaults to 514
 *          - transport - {Transport|String}        Defaults to Transport.UDP
 */
SysLogger.prototype.set = function(config) {
    config = config || {} ;

    this.setTag(config.tag);
    this.setFacility(config.facility);
    this.setHostname(config.hostname);
    this.setPort(config.port);
    this.setMessageComposer(config.messageComposer);
    if (config.hostname) {
        this.setTransport(Transport.UDP) ;
    } else {
        this.setTransport(config.transport) ;
    }

    return this;
};

SysLogger.prototype.setTransport = function(transport) {
    this.transport = transport || Transport.UDP;
    if (typeof this.transport == 'string') {
        this.transport = Transport[this.transport] ;
    }
    return this;
};

SysLogger.prototype.setTag = function(tag) {
    this.tag = tag || __filename;
    return this;
};
SysLogger.prototype.setFacility = function(facility) {
    this.facility = facility || Facility.user;
    if (typeof this.facility == 'string'){
        this.facility = Facility[this.facility];
    }
    return this;
};
SysLogger.prototype.setHostname = function(hostname) {
    if (hostname) {
      this.hostname = this.address = hostname;
    } else {
      this.hostname = DefaultHostname;
      this.address = DefaultAddress;
    }
    return this;
};

SysLogger.prototype.setPort = function(port) {
    this.port = port || 514;
    return this;
};

SysLogger.prototype.setMessageComposer = function(composerFunction){
    this.composerFunction = composerFunction || this.composeSyslogMessage;
    return this;
};

/**
 * Send message
 * @param {String} message
 * @param {Severity} severity
 */
SysLogger.prototype._send = function(message, severity) {
    this.transport(message, severity) ;
};

/**
 * Send formatted message to syslog
 * @param {String} message
 * @param {Number|String} severity
 */
SysLogger.prototype.send = function(message, severity) {
    severity = severity || Severity.notice;
    if (typeof severity == 'string'){
      severity = Severity[severity];
    }
    this._send(message, severity);
};

/**
 * Send log message with notice severity.
 */
SysLogger.prototype.log = function() {
    this._send(format.apply(this, arguments), Severity.notice);
};
/**
 * Send log message with info severity.
 */
SysLogger.prototype.info = function() {
    this._send(format.apply(this, arguments), Severity.info);
};
/**
 * Send log message with warn severity.
 */
SysLogger.prototype.warn = function() {
    this._send(format.apply(this, arguments), Severity.warn);
};
/**
 * Send log message with err severity.
 */
SysLogger.prototype.error = function() {
    this._send(format.apply(this, arguments), Severity.err);
};
/**
 * Send log message with debug severity.
 */
SysLogger.prototype.debug = function() {
    this._send(format.apply(this, arguments), Severity.debug);
};

/**
 * Compose syslog message
 */
SysLogger.prototype.composeSyslogMessage = function(message, severity) {
    return new Buffer('<' + (this.facility * 8 + severity) + '>' +
            this.getDate() + ' ' + this.hostname + ' ' +
            this.tag + '[' + process.pid + ']:' + message);
}

/**
 * Log object with `util.inspect` with notice severity
 */
SysLogger.prototype.dir = function(object) {
    var util = require('util');
    this._send(util.inspect(object) + '\n', Severity.notice);
};

SysLogger.prototype.time = function(label) {
    this._times[label] = Date.now();
};
SysLogger.prototype.timeEnd = function(label) {
    var duration = Date.now() - this._times[label];
    this.log('%s: %dms', label, duration);
};

SysLogger.prototype.trace = function(label) {
    var err = new Error();
    err.name = 'Trace';
    err.message = label || '';
    Error.captureStackTrace(err, arguments.callee);
    this.error(err.stack);
};

SysLogger.prototype.assert = function(expression) {
    if (!expression) {
        var arr = Array.prototype.slice.call(arguments, 1);
        this._send(format.apply(this, arr), Severity.err);
    }
};

/**
 * Get current date in syslog format. Thanks https://github.com/kordless/lodge
 * @returns {String}
 */
SysLogger.prototype.getDate = function() {
    var dt = new Date();
    var hours = this.leadZero(dt.getHours());
    var minutes = this.leadZero(dt.getMinutes());
    var seconds = this.leadZero(dt.getSeconds());
    var month = dt.getMonth();
    var day = dt.getDate();
    if(day < 10){
      day = ' ' + day;
    }
    var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
            'Sep', 'Oct', 'Nov', 'Dec' ];
    return months[month] + " " + day + " " + hours + ":" + minutes + ":" + seconds;
}

SysLogger.prototype.leadZero = function(n) {
    if (n < 10) {
        return '0' + n;
    } else {
        return n;
    }
}

module.exports = SysLogger;
<commit_msg>Fixed exception on Solaris even when not using the file transport<commit_after>var dgram = require('dgram');
var Buffer = require('buffer').Buffer;
var nodeConsole = console;

var DefaultHostname = require("os").hostname();
var DefaultAddress = "127.0.0.1";
var SingletonInstance = null;

 var socket
   , socketUsers = 0
   , releaseTimeout
   , socketErrorHandler = function (err) {
         if (err) {
             nodeConsole.error('socket error: ' + err)
         } else {
             nodeConsole.error('unknown socket error!')
         }

         if (socket !== undefined) {
             socket.close()
             socket = undefined
             socketUsers = 0
         }
     }
   , getSocket = function () {
         if (undefined === socket) {
             socket = dgram.createSocket('udp4')
             socket.on('error', socketErrorHandler)
         }
         ++socketUsers
         //console.log(socketUsers)
         return socket
     }
   , releaseSocket = function () {
         --socketUsers
         //console.log(socketUsers)
         if (0 == socketUsers && undefined === releaseTimeout) {
             releaseTimeout = setTimeout(function () {
                 if (0 == socketUsers && socket !== undefined) {
                     //console.log('closing socket!')
                     socket.close()
                     socket = undefined
                 } /*else {
                     console.log('another user attached while waiting to close the socket')
                 }*/
                 releaseTimeout = undefined
             }, 1000)
         }
     }

var Transport = {
    UDP: function(message, severity) {
        var self = this;
        var syslogMessage = this.composerFunction(message, severity);

        getSocket().send(syslogMessage,
                    0,
                    syslogMessage.length,
                    this.port,
                    this.address,
                    function(err, bytes) {
                      self._logError(err, bytes);
                      releaseSocket();
                    }
        );
    },

    file: (function() {
        var logTarget ;

        switch(require('os').type()) {
            case 'Darwin':
                logTarget = '/var/run/syslog' ;
                break ;

            case 'Linux':
                logTarget = '/dev/log' ;
                break ;
            default:
                logTarget = false ;
                break ;
        }

        return function(message, severity) {
            if (false === logTarget) {
                throw new Error('Unknown OS Type: ' + require('os').type()) ;
            }

            var client = dgram.createSocket('unix_dgram') ;
            var syslogMessage = this.composerFunction(message, severity);
            client.send(syslogMessage,
                        0,
                        syslogMessage.length,
                        logTarget,
                        this._logError
            );
            client.close() ;

        };
    })()
};

var Facility = {
    kern:   0,
    user:   1,
    mail:   2,
    daemon: 3,
    auth:   4,
    syslog: 5,
    lpr:    6,
    news:   7,
    uucp:   8,
    local0: 16,
    local1: 17,
    local2: 18,
    local3: 19,
    local4: 20,
    local5: 21,
    local6: 22,
    local7: 23
};

var Severity = {
    emerg:  0,
    alert:  1,
    crit:   2,
    err:    3,
    warn:   4,
    notice: 5,
    info:   6,
    debug:  7
};

// Format RegExp
var formatRegExp = /%[sdj]/g;

/**
 * Just copy from node.js console
 * @param f
 * @returns
 */
function format(f) {
  var   util = require('util'),
        i    = 0;

  if (typeof f !== 'string') {
    var objects = [];
    for (i = 0; i < arguments.length; i++) {
      objects.push(util.inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  i = 1;
  var args = arguments;
  var str = String(f).replace(formatRegExp, function(x) {
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j': return JSON.stringify(args[i++]);
      default:
        return x;
    }
  });
  for (var len = args.length, x = args[i]; i < len; x = args[++i]) {
    if (x === null || typeof x !== 'object') {
      str += ' ' + x;
    } else {
      str += ' ' + util.inspect(x);
    }
  }
  return str;
}

/**
 * Syslog logger
 * @constructor
 * @returns {SysLogger}
 */
function SysLogger(config) {
    this._times = {};
    this._logError = function(err, other) {
      if(err){
        nodeConsole.error('Cannot log message via %s:%d', this.hostname, this.port);
      }
    }.bind(this);
    this.set(config);
    return this;
}

/**
* Get singleton instance of SysLogger.
* @returns {SysLogger}
*/
SysLogger.getInstance = function() {
    if(!SingletonInstance){
        SingletonInstance = new SysLogger();
    }
    return SingletonInstance;
};

/**
 * Init function, takes a configuration object. If a hostname is provided the transport is assumed
 * to be Transport.UDP
 * @param {Object} configuration object with the following keys:
 *          - tag       - {String}                  By default is __filename
 *          - facility  - {Facility|Number|String}  By default is "user"
 *          - hostname  - {String}                  By default is require("os").hostname()
 *          - port      - {Number}                  Defaults to 514
 *          - transport - {Transport|String}        Defaults to Transport.UDP
 */
SysLogger.prototype.set = function(config) {
    config = config || {} ;

    this.setTag(config.tag);
    this.setFacility(config.facility);
    this.setHostname(config.hostname);
    this.setPort(config.port);
    this.setMessageComposer(config.messageComposer);
    if (config.hostname) {
        this.setTransport(Transport.UDP) ;
    } else {
        this.setTransport(config.transport) ;
    }

    return this;
};

SysLogger.prototype.setTransport = function(transport) {
    this.transport = transport || Transport.UDP;
    if (typeof this.transport == 'string') {
        this.transport = Transport[this.transport] ;
    }
    return this;
};

SysLogger.prototype.setTag = function(tag) {
    this.tag = tag || __filename;
    return this;
};
SysLogger.prototype.setFacility = function(facility) {
    this.facility = facility || Facility.user;
    if (typeof this.facility == 'string'){
        this.facility = Facility[this.facility];
    }
    return this;
};
SysLogger.prototype.setHostname = function(hostname) {
    if (hostname) {
      this.hostname = this.address = hostname;
    } else {
      this.hostname = DefaultHostname;
      this.address = DefaultAddress;
    }
    return this;
};

SysLogger.prototype.setPort = function(port) {
    this.port = port || 514;
    return this;
};

SysLogger.prototype.setMessageComposer = function(composerFunction){
    this.composerFunction = composerFunction || this.composeSyslogMessage;
    return this;
};

/**
 * Send message
 * @param {String} message
 * @param {Severity} severity
 */
SysLogger.prototype._send = function(message, severity) {
    this.transport(message, severity) ;
};

/**
 * Send formatted message to syslog
 * @param {String} message
 * @param {Number|String} severity
 */
SysLogger.prototype.send = function(message, severity) {
    severity = severity || Severity.notice;
    if (typeof severity == 'string'){
      severity = Severity[severity];
    }
    this._send(message, severity);
};

/**
 * Send log message with notice severity.
 */
SysLogger.prototype.log = function() {
    this._send(format.apply(this, arguments), Severity.notice);
};
/**
 * Send log message with info severity.
 */
SysLogger.prototype.info = function() {
    this._send(format.apply(this, arguments), Severity.info);
};
/**
 * Send log message with warn severity.
 */
SysLogger.prototype.warn = function() {
    this._send(format.apply(this, arguments), Severity.warn);
};
/**
 * Send log message with err severity.
 */
SysLogger.prototype.error = function() {
    this._send(format.apply(this, arguments), Severity.err);
};
/**
 * Send log message with debug severity.
 */
SysLogger.prototype.debug = function() {
    this._send(format.apply(this, arguments), Severity.debug);
};

/**
 * Compose syslog message
 */
SysLogger.prototype.composeSyslogMessage = function(message, severity) {
    return new Buffer('<' + (this.facility * 8 + severity) + '>' +
            this.getDate() + ' ' + this.hostname + ' ' +
            this.tag + '[' + process.pid + ']:' + message);
}

/**
 * Log object with `util.inspect` with notice severity
 */
SysLogger.prototype.dir = function(object) {
    var util = require('util');
    this._send(util.inspect(object) + '\n', Severity.notice);
};

SysLogger.prototype.time = function(label) {
    this._times[label] = Date.now();
};
SysLogger.prototype.timeEnd = function(label) {
    var duration = Date.now() - this._times[label];
    this.log('%s: %dms', label, duration);
};

SysLogger.prototype.trace = function(label) {
    var err = new Error();
    err.name = 'Trace';
    err.message = label || '';
    Error.captureStackTrace(err, arguments.callee);
    this.error(err.stack);
};

SysLogger.prototype.assert = function(expression) {
    if (!expression) {
        var arr = Array.prototype.slice.call(arguments, 1);
        this._send(format.apply(this, arr), Severity.err);
    }
};

/**
 * Get current date in syslog format. Thanks https://github.com/kordless/lodge
 * @returns {String}
 */
SysLogger.prototype.getDate = function() {
    var dt = new Date();
    var hours = this.leadZero(dt.getHours());
    var minutes = this.leadZero(dt.getMinutes());
    var seconds = this.leadZero(dt.getSeconds());
    var month = dt.getMonth();
    var day = dt.getDate();
    if(day < 10){
      day = ' ' + day;
    }
    var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
            'Sep', 'Oct', 'Nov', 'Dec' ];
    return months[month] + " " + day + " " + hours + ":" + minutes + ":" + seconds;
}

SysLogger.prototype.leadZero = function(n) {
    if (n < 10) {
        return '0' + n;
    } else {
        return n;
    }
}

module.exports = SysLogger;
<|endoftext|>


<commit_before>'use strict';

var intlpnCtrl = function( $ionicModal, $scope, intlpnUtils ) {
    var self = $scope;
    self.intlpnHelper = intlpnUtils.getHelper( self.onlyCountry);

    self._updateDialCode = function(newDialCode) {
        var newNumber;

        // save having to pass this every time
        newDialCode = "+" + newDialCode;

        if (self.phone) {
          // if the previous number contained a valid dial code, replace it
          // (if more than just a plus character)
          var prevDialCode = self.intlpnHelper.getDialCode(self.phone);
          if (prevDialCode.length > 1) {
            newNumber = self.phone.replace(prevDialCode, newDialCode);
          } else {
            // if the previous number didn't contain a dial code, we should persist it
            // XXX: remove jquery
            var existingNumber = (self.phone.charAt(0) != "+") ? $.trim(self.phone) : "";
            newNumber = newDialCode + existingNumber;
          }
        } else {
          newNumber = newDialCode;
        }

        self.dialCode = newDialCode;
        self.phone = newNumber;
    };

    self.codeFromPhone = function( number ) {
        return self.intlpnHelper.getFlagFromNumber(number);
    };

    self.isValid = function( number ) {
        return intlTelInputUtils.isValidNumber(number);
    }

    //default value
    if( !self.isocode && self.defaultCountry ) {
        self.isocode = self.defaultCountry;
        self.dialCode = '+' + self.intlpnHelper.dialCodesByIso[self.defaultCountry];
    }
};

angular.module('intlpnIonic', ['ionic'])
.service('intlpnUtils', function() {
    var intlpnUtilsHelper = function( onlyCountry) {
        var self = this;
        self.countries = [];
        self.countryCodes = [];
        self.dialCodesByIso={};
        self.onlyCountry = onlyCountry||[];
        self._addCountryCode = function(iso2, dialCode, priority) {
            if( !(iso2 in self.dialCodesByIso) ) {
                self.dialCodesByIso[iso2] = dialCode;
            }
            if (!(dialCode in self.countryCodes)) {
                self.countryCodes[dialCode] = [];
            }
            var index = priority || 0;
            self.countryCodes[dialCode][index] = iso2;
        };
        self._initCountries = function() {
            //only countries
            if(  self.onlyCountry.length ) {
                angular.forEach( allCountries, function(value) {
                     angular.forEach( self.onlyCountry, function( restricted ) {
                         if( restricted === value.iso2 )  {
                             self.countries.push( value );
                         }
                     });
                });
            } else {
                self.countries = allCountries;
            }
            for (i = 0; i < self.countries.length; i++) {
                  var c = self.countries[i];
                  self._addCountryCode(c.iso2, c.dialCode, c.priority);
                  // area codes
                  if (c.areaCodes) {
                    for (var j = 0; j < c.areaCodes.length; j++) {
                      // full dial code is country code + dial code
                      self._addCountryCode(c.iso2, c.dialCode + c.areaCodes[j]);
                    }
                  }
            };
        };
        self._getNumeric = function(s) {
            return s.replace(/\D/g, "");
        };
        self.getFlagFromNumber= function(number, updateDefault) {
            // try and extract valid dial code from input
            var dialCode = self.getDialCode(number);
            var countryCode = null;
            if (dialCode) {
              // check if one of the matching countries is already selected
              var codes = self.countryCodes[self._getNumeric(dialCode)];
              //alreadySelected = (this.selectedCountryData && $.inArray(this.selectedCountryData.iso2, countryCodes) != -1);
              // if a matching country is not already selected (or this is an unknown NANP area code): choose the first in the list
              //if (!alreadySelected || this._isUnknownNanp(number, dialCode)) {
                // if using onlyCountries option, countryCodes[0] may be empty, so we must find the first non-empty index
                for (var j = 0; j < codes.length; j++) {
                  if (codes[j]) {
                    countryCode = codes[j];
                    break;
                  }
                }
              //}
            } else if (number.charAt(0) == "+" && self._getNumeric(number).length) {
              // invalid dial code, so empty
              // Note: use getNumeric here because the number has not been formatted yet, so could contain bad shit
              countryCode = "";
            } else if (!number || number == "+") {
              // empty, or just a plus, so default
              countryCode = 'us';//this.options.defaultCountry.iso2;
            }

            if (countryCode !== null) {
              return countryCode;
            }
        };
        this.getDialCode= function(number) {
            var dialCode = "";
            // only interested in international numbers (starting with a plus)
            if (number && number.charAt(0) == "+") {
              var numericChars = "";
              // iterate over chars
              for (var i = 0; i < number.length; i++) {
                var c = number.charAt(i);
                // if char is number
                //XXX: replace by angular
                if ($.isNumeric(c)) {
                  numericChars += c;
                  // if current numericChars make a valid dial code
                  if (self.countryCodes[numericChars]) {
                    // store the actual raw string (useful for matching later)
                    dialCode = number.substr(0, i + 1);
                  }
                  // longest dial code is 4 chars
                  if (numericChars.length == 4) {
                    break;
                  }
                }
              }
            }
            return dialCode;
        };
        this.format_number = intlTelInputUtils.formatNumber;
    };
    return {
        getHelper: function( onlyCountry) {
            var helper = new intlpnUtilsHelper( onlyCountry);
            helper.allCountries = allCountries;
            helper._initCountries();
            return helper;
        }
    };
})
.directive('intlpnFormatter', function() {
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, ngModelController) {
            var el = element[0];

            function clean(x) {
                return intlTelInputUtils.formatNumber( x );
            }

            ngModelController.$parsers.push(function(val) {
                var offset = 0;
                if( !/^\+/.test( val ) ) {
                    val = '+'+val;
                    offset = 1;
                }
                var cleaned = clean(val);

                // Avoid infinite loop of $setViewValue <-> $parsers
                var newStart = cleaned.length;
                var start = el.selectionStart + offset;
                var end = el.selectionEnd + offset;
                var selectionLength = end - start;
                var digitsOnTheRight = 0;
                for( var i = val.length; i >= start; i-- ) {
                    if( /^\d$/.test( val.charAt(i) ) ) {
                        digitsOnTheRight ++;
                    }
                }
                while( digitsOnTheRight > 0 && newStart > 0 ) {
                    if( /^\d$/.test( cleaned.charAt( newStart - 1 ) ) ) {
                        digitsOnTheRight--;
                    }
                    newStart--;
                }
                //move to the left while space
                while(  /^[^0-9+]$/.test( cleaned.charAt( newStart - 1 ) ) && newStart > 0 )
                    newStart--;
                // element.val(cleaned) does not behave with
                // repeated invalid elements
                ngModelController.$setViewValue(cleaned);
                ngModelController.$render();
                el.setSelectionRange(newStart, newStart);
                return cleaned;
                //return val;
            });

        }
    }
})
.directive('intlpn', function( $ionicModal, $timeout ) {
    return {
        restrict: 'E',
        require: '^ngModel',
        scope: {
            ngModel: '=',
            placeholder: '@',
            defaultCountry: '@',
            onlyCountry: '='
        },
        controller: intlpnCtrl,
        link:function (scope, element, attrs, ngModelCtrl) {
            //When model value (outside world) is updated, set the view value (in ngModel directive)
            ngModelCtrl.$formatters.push(function(modelValue) {
                //from raw value to formatted value, parenthesis, dash, ...
                if( typeof modelValue === undefined || modelValue === '' ) {
                    $timeout(function() {
                        scope.isocode = scope.defaultCountry;
                        scope.dialCode = '+' + scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                    });
                }
                if( modelValue )
                    modelValue = modelValue.replace(/[^0-9]/g, "");
                return  modelValue?intlTelInputUtils.formatNumber('+'+modelValue):'';
            });
            //from the value in ngModel directive to my directive
            ngModelCtrl.$render = function() {
                scope.phone = ngModelCtrl.$viewValue;
                scope.dialCode = scope.intlpnHelper.getDialCode( ngModelCtrl.$viewValue );
                scope.isocode = scope.intlpnHelper.getFlagFromNumber( ngModelCtrl.$viewValue );

            };
            //from  view value (in ngModel directive) to model value (outside world)
            ngModelCtrl.$parsers.push(function(viewValue) {
                //clean everything that is not numeric or +
                viewValue = viewValue.replace(/[^0-9]/g, "");
                return viewValue?'+' + viewValue:'';
            });
            //$setViewValue, from my directive to view value (in ngModel directive)
            scope.$watch('phone', function(newValue, oldValue) {
                ngModelCtrl.$setViewValue( scope.phone );
                if( scope.intlpnHelper.getDialCode(  scope.phone ) ) {
                    scope.dialCode = scope.intlpnHelper.getDialCode(  scope.phone );
                    //from dialcode, validate current country
                    var countryCodes = scope.intlpnHelper.countryCodes[ scope.dialCode.replace(/[^0-9]/g, "") ];
                    var alreadySelected = (countryCodes.indexOf( scope.isocode ) > -1)?true:false;
                    if( !alreadySelected ) {
                        for (var j = 0; j < countryCodes.length; j++) {
                            if (countryCodes[j]) {
                                scope.isocode = countryCodes[j];
                                break;
                            }
                        }
                    }
                } else if( !scope.dialCode ) {
                    //default value
                    scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                }
            });
            scope.$watch('defaultCountry', function(newValue, oldValue) {
                if( !scope.phone || scope.phone === scope.dialCode ) {
                    scope.isocode = scope.defaultCountry;
                    scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                }
            });
            ngModelCtrl.$validators.validForm = function( modelValue, viewValue ) {
                //check if the cleaned value is correct
                return scope.isValid( modelValue );
            };
            //manage focus/blur of the phone field
            var input = element.find('input');
            input.bind('focus', function() {
                if( !scope.phone ) {
                    scope.$apply(function() {
                        scope.phone = scope.dialCode;
                        input[0].setSelectionRange( scope.dialCode.length );
                    });
                }
            })
            .bind('blur', function() {
                if( scope.phone === '+' ) {
                    scope.$apply(function() {
                        scope.isocode = scope.defaultCountry;
                        scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                        scope.phone = '';
                    });
                } else if( scope.phone === scope.dialCode || !scope.intlpnHelper.getDialCode(scope.phone) ) {
                    scope.$apply(function() {
                        scope.phone = '';
                    });
                }
            });
            var modalTemplate = '<ion-modal-view>' +
                '<ion-header-bar class="bar-positive">' +
                    '
Search
' +
                    '<button class="button button-clear icon ion-ios-close-empty" ng-click="modalScope.close()"></button>' +
                '</ion-header-bar>' +
                    '
' +
                        '
' +
                            '' +
                            '<input type="text" autocorrect="off" autocomplete="off" autocapitalize="off" spellcheck="false" placeholder="Search" ng-model="modalScope.pattern" ng-change="modalScope.scrollTop()">' +
                            '' +
                        '
' +
                    '
' +
                 '<ion-content class="has-header has-subheader">' +
                    '<ion-list>' +
                        '<ion-item ng-repeat="country in modalScope.countries | filter:modalScope.pattern" ' +
                            'ng-click="modalScope.selectCountry( country )" ' +
                            'class="item-icon-left" ng-class="(country.iso2 == modalScope.currentCountry)?\'item-icon-right\':\'\'">' +
                                '' +
                                '{{::country.name}}' +
                                '' +
                        '</ion-item>' +
                    '</ion-list>' +
                '</ion-content>' +
                '</ion-modal-view>';
            scope.modalScope = {
                selectCountry: function( country ) {
                    scope.isocode = country.iso2;
                    scope._updateDialCode( country.dialCode );
                    scope.modal.hide();
                    $timeout(function() { input.focus();});
                },
                close: function() {
                    scope.modal.hide();
                },
                scrollTop: function() {
                    $ionicScrollDelegate.scrollTop(true);
                },
                countries: scope.intlpnHelper.countries
            };
            scope.modal = $ionicModal.fromTemplate( modalTemplate, {
                scope: scope
            });
            scope.pickCountry = function() {
                scope.modalScope.pattern = '';
                scope.modalScope.currentCountry = scope.isocode;
                scope.modal.show();
            };
        },
        replace:true,
        template: '
' +
                        ''+
                        '<input intlpn-formatter type="tel" placeholder="{{placeholder||\'test\'}}" ng-model="phone" >' +
                '
'
    };
})
<commit_msg>stupid mistake in dependency injection<commit_after>'use strict';

var intlpnCtrl = function( $ionicModal, $scope, intlpnUtils ) {
    var self = $scope;
    self.intlpnHelper = intlpnUtils.getHelper( self.onlyCountry);

    self._updateDialCode = function(newDialCode) {
        var newNumber;

        // save having to pass this every time
        newDialCode = "+" + newDialCode;

        if (self.phone) {
          // if the previous number contained a valid dial code, replace it
          // (if more than just a plus character)
          var prevDialCode = self.intlpnHelper.getDialCode(self.phone);
          if (prevDialCode.length > 1) {
            newNumber = self.phone.replace(prevDialCode, newDialCode);
          } else {
            // if the previous number didn't contain a dial code, we should persist it
            // XXX: remove jquery
            var existingNumber = (self.phone.charAt(0) != "+") ? $.trim(self.phone) : "";
            newNumber = newDialCode + existingNumber;
          }
        } else {
          newNumber = newDialCode;
        }

        self.dialCode = newDialCode;
        self.phone = newNumber;
    };

    self.codeFromPhone = function( number ) {
        return self.intlpnHelper.getFlagFromNumber(number);
    };

    self.isValid = function( number ) {
        return intlTelInputUtils.isValidNumber(number);
    }

    //default value
    if( !self.isocode && self.defaultCountry ) {
        self.isocode = self.defaultCountry;
        self.dialCode = '+' + self.intlpnHelper.dialCodesByIso[self.defaultCountry];
    }
};

angular.module('intlpnIonic', ['ionic'])
.service('intlpnUtils', function() {
    var intlpnUtilsHelper = function( onlyCountry) {
        var self = this;
        self.countries = [];
        self.countryCodes = [];
        self.dialCodesByIso={};
        self.onlyCountry = onlyCountry||[];
        self._addCountryCode = function(iso2, dialCode, priority) {
            if( !(iso2 in self.dialCodesByIso) ) {
                self.dialCodesByIso[iso2] = dialCode;
            }
            if (!(dialCode in self.countryCodes)) {
                self.countryCodes[dialCode] = [];
            }
            var index = priority || 0;
            self.countryCodes[dialCode][index] = iso2;
        };
        self._initCountries = function() {
            //only countries
            if(  self.onlyCountry.length ) {
                angular.forEach( allCountries, function(value) {
                     angular.forEach( self.onlyCountry, function( restricted ) {
                         if( restricted === value.iso2 )  {
                             self.countries.push( value );
                         }
                     });
                });
            } else {
                self.countries = allCountries;
            }
            for (i = 0; i < self.countries.length; i++) {
                  var c = self.countries[i];
                  self._addCountryCode(c.iso2, c.dialCode, c.priority);
                  // area codes
                  if (c.areaCodes) {
                    for (var j = 0; j < c.areaCodes.length; j++) {
                      // full dial code is country code + dial code
                      self._addCountryCode(c.iso2, c.dialCode + c.areaCodes[j]);
                    }
                  }
            };
        };
        self._getNumeric = function(s) {
            return s.replace(/\D/g, "");
        };
        self.getFlagFromNumber= function(number, updateDefault) {
            // try and extract valid dial code from input
            var dialCode = self.getDialCode(number);
            var countryCode = null;
            if (dialCode) {
              // check if one of the matching countries is already selected
              var codes = self.countryCodes[self._getNumeric(dialCode)];
              //alreadySelected = (this.selectedCountryData && $.inArray(this.selectedCountryData.iso2, countryCodes) != -1);
              // if a matching country is not already selected (or this is an unknown NANP area code): choose the first in the list
              //if (!alreadySelected || this._isUnknownNanp(number, dialCode)) {
                // if using onlyCountries option, countryCodes[0] may be empty, so we must find the first non-empty index
                for (var j = 0; j < codes.length; j++) {
                  if (codes[j]) {
                    countryCode = codes[j];
                    break;
                  }
                }
              //}
            } else if (number.charAt(0) == "+" && self._getNumeric(number).length) {
              // invalid dial code, so empty
              // Note: use getNumeric here because the number has not been formatted yet, so could contain bad shit
              countryCode = "";
            } else if (!number || number == "+") {
              // empty, or just a plus, so default
              countryCode = 'us';//this.options.defaultCountry.iso2;
            }

            if (countryCode !== null) {
              return countryCode;
            }
        };
        this.getDialCode= function(number) {
            var dialCode = "";
            // only interested in international numbers (starting with a plus)
            if (number && number.charAt(0) == "+") {
              var numericChars = "";
              // iterate over chars
              for (var i = 0; i < number.length; i++) {
                var c = number.charAt(i);
                // if char is number
                //XXX: replace by angular
                if ($.isNumeric(c)) {
                  numericChars += c;
                  // if current numericChars make a valid dial code
                  if (self.countryCodes[numericChars]) {
                    // store the actual raw string (useful for matching later)
                    dialCode = number.substr(0, i + 1);
                  }
                  // longest dial code is 4 chars
                  if (numericChars.length == 4) {
                    break;
                  }
                }
              }
            }
            return dialCode;
        };
        this.format_number = intlTelInputUtils.formatNumber;
    };
    return {
        getHelper: function( onlyCountry) {
            var helper = new intlpnUtilsHelper( onlyCountry);
            helper.allCountries = allCountries;
            helper._initCountries();
            return helper;
        }
    };
})
.directive('intlpnFormatter', function() {
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, ngModelController) {
            var el = element[0];

            function clean(x) {
                return intlTelInputUtils.formatNumber( x );
            }

            ngModelController.$parsers.push(function(val) {
                var offset = 0;
                if( !/^\+/.test( val ) ) {
                    val = '+'+val;
                    offset = 1;
                }
                var cleaned = clean(val);

                // Avoid infinite loop of $setViewValue <-> $parsers
                var newStart = cleaned.length;
                var start = el.selectionStart + offset;
                var end = el.selectionEnd + offset;
                var selectionLength = end - start;
                var digitsOnTheRight = 0;
                for( var i = val.length; i >= start; i-- ) {
                    if( /^\d$/.test( val.charAt(i) ) ) {
                        digitsOnTheRight ++;
                    }
                }
                while( digitsOnTheRight > 0 && newStart > 0 ) {
                    if( /^\d$/.test( cleaned.charAt( newStart - 1 ) ) ) {
                        digitsOnTheRight--;
                    }
                    newStart--;
                }
                //move to the left while space
                while(  /^[^0-9+]$/.test( cleaned.charAt( newStart - 1 ) ) && newStart > 0 )
                    newStart--;
                // element.val(cleaned) does not behave with
                // repeated invalid elements
                ngModelController.$setViewValue(cleaned);
                ngModelController.$render();
                el.setSelectionRange(newStart, newStart);
                return cleaned;
                //return val;
            });

        }
    }
})
.directive('intlpn', function( $ionicModal, $timeout, $ionicScrollDelegate ) {
    return {
        restrict: 'E',
        require: '^ngModel',
        scope: {
            ngModel: '=',
            placeholder: '@',
            defaultCountry: '@',
            onlyCountry: '='
        },
        controller: intlpnCtrl,
        link:function (scope, element, attrs, ngModelCtrl) {
            //When model value (outside world) is updated, set the view value (in ngModel directive)
            ngModelCtrl.$formatters.push(function(modelValue) {
                //from raw value to formatted value, parenthesis, dash, ...
                if( typeof modelValue === undefined || modelValue === '' ) {
                    $timeout(function() {
                        scope.isocode = scope.defaultCountry;
                        scope.dialCode = '+' + scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                    });
                }
                if( modelValue )
                    modelValue = modelValue.replace(/[^0-9]/g, "");
                return  modelValue?intlTelInputUtils.formatNumber('+'+modelValue):'';
            });
            //from the value in ngModel directive to my directive
            ngModelCtrl.$render = function() {
                scope.phone = ngModelCtrl.$viewValue;
                scope.dialCode = scope.intlpnHelper.getDialCode( ngModelCtrl.$viewValue );
                scope.isocode = scope.intlpnHelper.getFlagFromNumber( ngModelCtrl.$viewValue );

            };
            //from  view value (in ngModel directive) to model value (outside world)
            ngModelCtrl.$parsers.push(function(viewValue) {
                //clean everything that is not numeric or +
                viewValue = viewValue.replace(/[^0-9]/g, "");
                return viewValue?'+' + viewValue:'';
            });
            //$setViewValue, from my directive to view value (in ngModel directive)
            scope.$watch('phone', function(newValue, oldValue) {
                ngModelCtrl.$setViewValue( scope.phone );
                if( scope.intlpnHelper.getDialCode(  scope.phone ) ) {
                    scope.dialCode = scope.intlpnHelper.getDialCode(  scope.phone );
                    //from dialcode, validate current country
                    var countryCodes = scope.intlpnHelper.countryCodes[ scope.dialCode.replace(/[^0-9]/g, "") ];
                    var alreadySelected = (countryCodes.indexOf( scope.isocode ) > -1)?true:false;
                    if( !alreadySelected ) {
                        for (var j = 0; j < countryCodes.length; j++) {
                            if (countryCodes[j]) {
                                scope.isocode = countryCodes[j];
                                break;
                            }
                        }
                    }
                } else if( !scope.dialCode ) {
                    //default value
                    scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                }
            });
            scope.$watch('defaultCountry', function(newValue, oldValue) {
                if( !scope.phone || scope.phone === scope.dialCode ) {
                    scope.isocode = scope.defaultCountry;
                    scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                }
            });
            ngModelCtrl.$validators.validForm = function( modelValue, viewValue ) {
                //check if the cleaned value is correct
                return scope.isValid( modelValue );
            };
            //manage focus/blur of the phone field
            var input = element.find('input');
            input.bind('focus', function() {
                if( !scope.phone ) {
                    scope.$apply(function() {
                        scope.phone = scope.dialCode;
                        input[0].setSelectionRange( scope.dialCode.length );
                    });
                }
            })
            .bind('blur', function() {
                if( scope.phone === '+' ) {
                    scope.$apply(function() {
                        scope.isocode = scope.defaultCountry;
                        scope.dialCode = "+"+scope.intlpnHelper.dialCodesByIso[scope.defaultCountry];
                        scope.phone = '';
                    });
                } else if( scope.phone === scope.dialCode || !scope.intlpnHelper.getDialCode(scope.phone) ) {
                    scope.$apply(function() {
                        scope.phone = '';
                    });
                }
            });
            var modalTemplate = '<ion-modal-view>' +
                '<ion-header-bar class="bar-positive">' +
                    '
Search
' +
                    '<button class="button button-clear icon ion-ios-close-empty" ng-click="modalScope.close()"></button>' +
                '</ion-header-bar>' +
                    '
' +
                        '
' +
                            '' +
                            '<input type="text" autocorrect="off" autocomplete="off" autocapitalize="off" spellcheck="false" placeholder="Search" ng-model="modalScope.pattern" ng-change="modalScope.scrollTop()">' +
                            '' +
                        '
' +
                    '
' +
                 '<ion-content class="has-header has-subheader">' +
                    '<ion-list>' +
                        '<ion-item ng-repeat="country in modalScope.countries | filter:modalScope.pattern" ' +
                            'ng-click="modalScope.selectCountry( country )" ' +
                            'class="item-icon-left" ng-class="(country.iso2 == modalScope.currentCountry)?\'item-icon-right\':\'\'">' +
                                '' +
                                '{{::country.name}}' +
                                '' +
                        '</ion-item>' +
                    '</ion-list>' +
                '</ion-content>' +
                '</ion-modal-view>';
            scope.modalScope = {
                selectCountry: function( country ) {
                    scope.isocode = country.iso2;
                    scope._updateDialCode( country.dialCode );
                    scope.modal.hide();
                    $timeout(function() { input.focus();});
                },
                close: function() {
                    scope.modal.hide();
                },
                scrollTop: function() {
                    $ionicScrollDelegate.scrollTop(true);
                },
                countries: scope.intlpnHelper.countries
            };
            scope.modal = $ionicModal.fromTemplate( modalTemplate, {
                scope: scope
            });
            scope.pickCountry = function() {
                scope.modalScope.pattern = '';
                scope.modalScope.currentCountry = scope.isocode;
                scope.modal.show();
            };
        },
        replace:true,
        template: '
' +
                        ''+
                        '<input intlpn-formatter type="tel" placeholder="{{placeholder||\'test\'}}" ng-model="phone" >' +
                '
'
    };
})
<|endoftext|>


<commit_before>/**
 * 为常用的 button 组件添加点击效果，同时全局增加 tap 事件
 *
 * @author      mufeng  <smufeng@gmail.com>
 * @version     0.2     <2016-01-19>
 */

$(function() {
    var $document = $(document), tap, fastmove, input;

    /* 修复path对象，无则自己模拟一个出来 */
    function fixPath(e) {
        var target = e.target, arr = [];

        while (target && target.parentNode) {
            arr.push(target);   // 当前存下来

            target = target.parentNode;
        }

        return arr;     // 返回创建的对象
    }

    function checkClass(item, test) {
        var find, cls = item.attr ? item.attr("class") :
                  item.getAttribute || item.getAttribute("class");

        /* 转换为数组，便于后面操作 */
        cls  = (cls || "").split(" ");
        find = (test || "button item tab-item").split(" ");

        for (var i=0; i<find.length; i++) {
            if (cls.indexOf(find[i]) >= 0) return true;
        }

        return false;
    }

    // 清除激活类
    function clearActive($target) {
        if (checkClass($target)) {
            $target.removeClass("active");
        }
    }
    $._ui_clearActive = clearActive;

    tap = {
        delay  : 200,
        animate: 240,

        /**
         * checkMove 点击移动判断容错率
         *
         * checkMoveBad 是针对移动设备
         *
         * 提高容错率可以提高点击的成功率，但
         * 同时也可能会导致误点几率变大
         * 国产山寨设备建议设置为 30以上
         */
        checkMove: 4,
        checkMoveBad: 30,

        doubleTime: 300,
        delayClass: 80,

        /* 为true会在移动后，清除元素class */
        moveClear: true,
        hasClear : false,

        input: null,
        target: null,
        lastType: "",
        disClick: false,
        delayStart: null,
        tapIsStart: false,

        startX: 0,
        startY: 0,
        startTime: null,

        _focus: function(e) {
            this.input = e.target;
        },

        _isMove: function(e, move) {
            var mv = move ? move : this.checkMove,
                touch = e.changedTouches, cx, cy;

            // 如果不是精确点击事件，采用加大的值
            if (!move && !e.type.match(/^mouse|pinter/)) {
                mv = this.checkMoveBad;
            }

            touch = touch && touch[0] ? touch[0] : e;
            cx = Math.abs(touch.pageX - this.startX);
            cy = Math.abs(touch.pageY - this.startY);

            return !!(cx >= mv || cy >= mv);
        },

        _isOne: function(now, old) {
            var reg = /^mouse|touch|pointer/;

            old = old.match(reg);
            now = now.match(reg);

            return old && now && old[0] == now[0];
        },

        _addActive: function(e) {
            var path = fixPath(e);

            for (var i = 0; i<path.length; i++) {
                var $item = $(path[i]);

                if (checkClass($item)) {
                    $item.addClass("active");
                }
            }
        },

        _clearActive: function(e) {
            var path = fixPath(e);

            for(var i = 0; i<path.length; i++) {
                clearActive($(path[i]));
            }
        },

        _start: function(e) {
            var touch = e.touches ? e.touches[0] : e, ct,
                tagName, path = fixPath(e), ltype = this.lastType;

            /* 修复移动端input点击焦点不更新的问题 */
            if (e.target != this.input) {
                this.input && this.input.blur();        // 刷新焦点
                this.input = null;
            }

            ct = $.getTime() - this.startTime;

            if ((e.touches && e.touches.length > 1) ||
                (!this._isOne(e.type, ltype) && ct < 600) ) {
                this.startX = null;      this.startY = null;

                return true;
            }

            this.lastType = e.type;
            tagName = e.target.tagName;
            $(e.target).trigger("tapstart", e);

            /* 记录此次点击事件的相关信息，用于方法判断 */
            this.startX = touch.pageX;
            this.startY = touch.pageY;
            this.startTime = $.getTime();
            this.target    = e.target;
            this.hasClear  = false;

            // 修复 BUTTON元素 出现点击两次的问题
            for (var i = 0; i<path.length; i++) {
                var $item = $(path[i]), type;
                tagName = $item[0].tagName.toUpperCase();
                type    = ($item.attr("type") || "").toUpperCase();

                if (tagName == "BUTTON" || (tagName == "INPUT" &&
                    "RADIO CHECKBOX".split(" ").indexOf(type) >= 0) ) {
                    e.preventDefault();
                    break;
                }
            }

            if (this.moveClear) {
                this.delayStart = setTimeout(function() {
                    /* 给按钮类的组件添加点击样式 */
                    tap._addActive(e);
                }, this.delayClass);
            } else {
                tap._addActive(e);
            }
        },

        _move: function(e) {
            e.preventDefault(); // 修复微信下拉显示网页地址

            // 创建 tapmove 事件
            if (this.startX != null) {
                $(e.target).trigger("tapmove", e);
            }

            if (this.moveClear && !this.hasClear && this._isMove(e)) {
                // 清除 tap 自定义相关操作
                clearTimeout(this.delayStart);

                // 清除事件中相关的元素的激活类
                tap._clearActive(e);
                this.hasClear = true;
            }
        },

        _end: function(e) {
            var ct, $target = $(e.target);

            ct = $.getTime() - this.startTime;

            if (this.startX != null) {
                $target.trigger("tapend", e);
            }

            if (!this._isMove(e) && ct < this.delay) {
                this.disClick = false;
                $target.trigger("click");
                $target.trigger("tap");
            }

            setTimeout(function() {
                tap._clearActive(e);
            }, this.delayClass*1.2);
        },

        handleEvent: function (e) {
            switch ( e.type ) {
                case 'touchstart':
                case 'pointerdown':
                case 'MSPointerDown':
                case 'mousedown':
                    this._start(e);
                    break;
                case 'touchmove':
                case 'pointermove':
                case 'MSPointerMove':
                case 'mousemove':
                    this._move(e);
                    break;
                case 'touchend':
                case 'pointerup':
                case 'MSPointerUp':
                case 'mouseup':
                case 'touchcancel':
                case 'pointercancel':
                case 'MSPointerCancel':
                case 'mousecancel':
                    this._end(e);
                    break;
            }
        }
    };

    /**
     *
     * 事件初始化，同时修复移动端 点击穿透问题，原理如下:
     *
     * 移动端的click都是touch之后，才会模拟触发。触发的顺序如下:
     *
     * touchstart -> touchmove -> touchend ->
     * mousedown  -> mousemove -> mouseenter -> click
     *
     * 在重叠的区域里，被遮盖的元素绑定click，遮盖的元素绑定touch事件，
     * 且touch后遮盖的元素会隐藏的话，就会造成穿透，因为click是在touch
     * 之后延迟触发的，浏览器会误认为是在遮盖的元素上触发了click。
     *
     */
    ;(function() {
        var kt = window.ontouchstart !== undefined ? 0 : 1,
            doc = document, bind = "addEventListener";

        /* 修复input 焦点问题 */
        doc[bind]("focus", tap._focus, true)

        doc[bind]("touchstart", tap);
        doc[bind]("touchmove", tap);
        doc[bind]("touchend", tap);
        doc[bind]("touchcancel", tap);

        doc[bind]("mousedown", tap);
        doc[bind]("mousemove", tap);
        doc[bind]("mouseup", tap);
        doc[bind]("mousecancel", tap);

        doc[bind]("pointerdown", tap);
        doc[bind]("pointermove", tap);
        doc[bind]("pointerup", tap);
        doc[bind]("pointercancel", tap);

        doc[bind]("MSPointerDown", tap);
        doc[bind]("MSPointerMove", tap);
        doc[bind]("MSPointerUp", tap);
        doc[bind]("MSPointerCancel", tap);

        $document.on("click", function(e) {
            if (!tap.disClick &&
                (e.timeStamp - tap.startTime) < 300) {

                // checkbox radio 元素，点击后value值修复
                var tar = e.target, $tar = $(tar), type;

                if (tar.tagName == "INPUT") {
                    type = tar.type.toUpperCase();

                    if (["RADIO", "CHECKBOX"].findIn(type)) {
                        if (tar.checked === true) {
                            $tar.val(false);
                            $tar.removeClass("checked");
                        } else if (type === "CHECKBOX") {
                            $tar.val(true);
                            $tar.addClass("checked");
                        }
                    }
                }

                tap.disClick = true;
            } else {
                e.preventDefault();
                e.stopImmediatePropagation();
            }
        }, true);
    })(window, tap);
});
<commit_msg>tap优化<commit_after>/**
 * 为常用的 button 组件添加点击效果，同时全局增加 tap 事件
 *
 * @author      mufeng  <smufeng@gmail.com>
 * @version     0.2     <2016-01-19>
 */

$(function() {
    var $document = $(document), tap, fastmove, input;

    /* 修复path对象，无则自己模拟一个出来 */
    function fixPath(e) {
        var target = e.target, arr = [];

        while (target && target.parentNode) {
            arr.push(target);   // 当前存下来

            target = target.parentNode;
        }

        return arr;     // 返回创建的对象
    }

    function checkClass(item, test) {
        var find, cls = item.attr ? item.attr("class") :
                  item.getAttribute || item.getAttribute("class");

        /* 转换为数组，便于后面操作 */
        cls  = (cls || "").split(" ");
        find = (test || "button item tab-item").split(" ");

        for (var i=0; i<find.length; i++) {
            if (cls.indexOf(find[i]) >= 0) return true;
        }

        return false;
    }

    // 清除激活类
    function clearActive($target) {
        if (checkClass($target)) {
            $target.removeClass("active");
        }
    }
    $._ui_clearActive = clearActive;

    tap = {
        delay  : 200,
        animate: 240,

        /**
         * checkMove 点击移动判断容错率
         *
         * checkMoveBad 是针对移动设备
         *
         * 提高容错率可以提高点击的成功率，但
         * 同时也可能会导致误点几率变大
         * 国产山寨设备建议设置为 30以上
         */
        checkMove: 4,
        checkMoveBad: 30,

        doubleTime: 300,
        delayClass: 80,

        /* 为true会在移动后，清除元素class */
        moveClear: true,
        hasClear : false,

        input: null,
        target: null,
        lastType: "",
        disClick: false,
        delayStart: null,
        tapIsStart: false,

        startX: 0,
        startY: 0,
        startTime: null,

        _focus: function(e) {
            this.input = e.target;
        },

        _isMove: function(e, move) {
            var mv = move ? move : this.checkMove,
                touch = e.changedTouches, cx, cy;

            // 如果不是精确点击事件，采用加大的值
            if (!move && !e.type.match(/^mouse|pinter/)) {
                mv = this.checkMoveBad;
            }

            touch = touch && touch[0] ? touch[0] : e;
            cx = Math.abs(touch.pageX - this.startX);
            cy = Math.abs(touch.pageY - this.startY);

            return !!(cx >= mv || cy >= mv);
        },

        _isOne: function(now, old) {
            var reg = /^mouse|touch|pointer/;

            old = old.match(reg);
            now = now.match(reg);

            return old && now && old[0] == now[0];
        },

        _addActive: function(e) {
            var path = fixPath(e);

            for (var i = 0; i<path.length; i++) {
                var $item = $(path[i]);

                if (checkClass($item)) {
                    $item.addClass("active");
                }
            }
        },

        _clearActive: function(e) {
            var path = fixPath(e);

            for(var i = 0; i<path.length; i++) {
                clearActive($(path[i]));
            }
        },

        _start: function(e) {
            var touch = e.touches ? e.touches[0] : e, ct,
                tagName, path = fixPath(e), ltype = this.lastType;

            /* 修复移动端input点击焦点不更新的问题 */
            if (e.target != this.input) {
                this.input && this.input.blur();        // 刷新焦点
                this.input = null;
            }

            ct = $.getTime() - this.startTime;

            if ((e.touches && e.touches.length > 1) ||
                (!this._isOne(e.type, ltype) && ct < 600) ) {
                this.startX = null;      this.startY = null;

                return true;
            }

            this.lastType = e.type;
            tagName = e.target.tagName;
            $(e.target).trigger("tapstart", e);

            /* 记录此次点击事件的相关信息，用于方法判断 */
            this.startX = touch.pageX;
            this.startY = touch.pageY;
            this.startTime = $.getTime();
            this.target    = e.target;
            this.hasClear  = false;

            // 修复 BUTTON元素 出现点击两次的问题
            for (var i = 0; i<path.length; i++) {
                var $item = $(path[i]), type;
                tagName = $item[0].tagName.toUpperCase();
                type    = ($item.attr("type") || "").toUpperCase();

                if (tagName == "BUTTON" || (tagName == "INPUT" &&
                    "RADIO CHECKBOX".split(" ").indexOf(type) >= 0) ) {
                    e.preventDefault();
                    break;
                }
            }

            // 清除上一次的状态，解决双击问题
            clearTimeout(this.delayStart);
            this._clearActive(e);

            // 给按钮类的组件添加点击样式
            if (this.moveClear) {
                this.delayStart = setTimeout(function() {
                    tap._addActive(e);
                }, this.delayClass);
            } else {
                tap._addActive(e);
            }
        },

        _move: function(e) {
            e.preventDefault(); // 修复微信下拉显示网页地址

            // 创建 tapmove 事件
            if (this.startX != null) {
                $(e.target).trigger("tapmove", e);
            }

            if (this.moveClear && !this.hasClear && this._isMove(e)) {
                // 清除 tap 自定义相关操作
                clearTimeout(this.delayStart);

                // 清除事件中相关的元素的激活类
                tap._clearActive(e);
                this.hasClear = true;
            }
        },

        _end: function(e) {
            var ct, $target = $(e.target);

            ct = $.getTime() - this.startTime;

            if (this.startX != null) {
                $target.trigger("tapend", e);
            }

            if (!this._isMove(e) && ct < this.delay) {
                this.disClick = false;
                $target.trigger("click");
                $target.trigger("tap");
            }

            setTimeout(function() {
                tap._clearActive(e);
            }, this.delayClass*1.2);
        },

        handleEvent: function (e) {
            switch ( e.type ) {
                case 'touchstart':
                case 'pointerdown':
                case 'MSPointerDown':
                case 'mousedown':
                    this._start(e);
                    break;
                case 'touchmove':
                case 'pointermove':
                case 'MSPointerMove':
                case 'mousemove':
                    this._move(e);
                    break;
                case 'touchend':
                case 'pointerup':
                case 'MSPointerUp':
                case 'mouseup':
                case 'touchcancel':
                case 'pointercancel':
                case 'MSPointerCancel':
                case 'mousecancel':
                    this._end(e);
                    break;
            }
        }
    };

    /**
     *
     * 事件初始化，同时修复移动端 点击穿透问题，原理如下:
     *
     * 移动端的click都是touch之后，才会模拟触发。触发的顺序如下:
     *
     * touchstart -> touchmove -> touchend ->
     * mousedown  -> mousemove -> mouseenter -> click
     *
     * 在重叠的区域里，被遮盖的元素绑定click，遮盖的元素绑定touch事件，
     * 且touch后遮盖的元素会隐藏的话，就会造成穿透，因为click是在touch
     * 之后延迟触发的，浏览器会误认为是在遮盖的元素上触发了click。
     *
     */
    ;(function() {
        var kt = window.ontouchstart !== undefined ? 0 : 1,
            doc = document, bind = "addEventListener";

        /* 修复input 焦点问题 */
        doc[bind]("focus", tap._focus, true)

        doc[bind]("touchstart", tap);
        doc[bind]("touchmove", tap);
        doc[bind]("touchend", tap);
        doc[bind]("touchcancel", tap);

        doc[bind]("mousedown", tap);
        doc[bind]("mousemove", tap);
        doc[bind]("mouseup", tap);
        doc[bind]("mousecancel", tap);

        doc[bind]("pointerdown", tap);
        doc[bind]("pointermove", tap);
        doc[bind]("pointerup", tap);
        doc[bind]("pointercancel", tap);

        doc[bind]("MSPointerDown", tap);
        doc[bind]("MSPointerMove", tap);
        doc[bind]("MSPointerUp", tap);
        doc[bind]("MSPointerCancel", tap);

        $document.on("click", function(e) {
            if (!tap.disClick &&
                (e.timeStamp - tap.startTime) < 300) {

                // checkbox radio 元素，点击后value值修复
                var tar = e.target, $tar = $(tar), type;

                if (tar.tagName == "INPUT") {
                    type = tar.type.toUpperCase();

                    if (["RADIO", "CHECKBOX"].findIn(type)) {
                        if (tar.checked === true) {
                            $tar.val(false);
                            $tar.removeClass("checked");
                        } else if (type === "CHECKBOX") {
                            $tar.val(true);
                            $tar.addClass("checked");
                        }
                    }
                }

                tap.disClick = true;
            } else {
                e.preventDefault();
                e.stopImmediatePropagation();
            }
        }, true);
    })(window, tap);
});
<|endoftext|>


<commit_before>
var e = require('./lib/odyssey/story');
e.Actions = require('./lib/odyssey/actions');
e.Triggers = require('./lib/odyssey/triggers');
e.Core = require('./lib/odyssey/core');
e.Template = require('./lib/odyssey/template');
e.UI = require('./lib/odyssey/ui');

for (var k in e.Actions) {
  e[k] = e.Actions[k];
}
for (var k in e.Triggers) {
  e[k] = e.Triggers[k];
}
module.exports = e;
<commit_msg>missing index<commit_after>
var e = require('./lib/odyssey/story');
e.Actions = require('./lib/odyssey/actions');
e.Triggers = require('./lib/odyssey/triggers');
e.Core = require('./lib/odyssey/core');
e.Template = require('./lib/odyssey/template');
e.UI = require('./lib/odyssey/ui');
require('./lib/odyssey/util');

for (var k in e.Actions) {
  e[k] = e.Actions[k];
}
for (var k in e.Triggers) {
  e[k] = e.Triggers[k];
}
module.exports = e;
<|endoftext|>


<commit_before>import {
    area,
    line,
} from 'd3-shape';
import { interpolateArray } from 'd3-interpolate';
import { timer } from 'd3-timer';
import { mouse } from 'd3-selection';
import {
    uuid,
    linearStops
} from 'vizart-core';

import { AbstractBasicCartesianChartWithAxes } from '../../base';
import createCartesianOpt from '../../options/createCartesianOpt';
import interpolateCurve from '../../util/curve';
import applyQuadtree from './quadtree/apply';
import applyVoronoi from './voronoi';
import gradientStroke from './gradient-stroke';
import genColorByIndex from './generate-color';

const AreaOpt = {
    chart: {
        type: 'area_horizontal'
    },
    plots: {
        areaOpacity: 1,
        curve: 'linear',
        strokeWidth: 4,
        nodeRadius: 4,
        drawArea: true,
        showDots: true
    }
}

const nodeColor = opt=> {
    const stops = linearStops(opt.color.scheme);
    return stops[stops.length - 1].color;
}

/**
 * a particle contains x, y, r, c, alpha
 *
 * @param context
 * @param particles, particle colors may be defined in rgb string and thus cannot be recognized by
 * canvas. This is caused by d3's interpolation.
 */
const draw = (context, particles, width, height, opt, hidden = false)=> {
    context.clearRect(0, 0, width, height);

    if (opt.plots.showDots === true) {
        for (const [i, p] of particles.entries()) {
            context.beginPath();
            context.fillStyle = hidden === true? genColorByIndex(i) : nodeColor(opt);
            context.globalAlpha = p.alpha;
            context.arc(p.x, p.y, p.r, 0, 2 * Math.PI, false);
            context.fill();
        }
    }

    const curve = (opt.plots.drawArea === true)
        ? area()
            .x(d=>d.x)
            .y0(opt.chart.innerHeight)
            .y1(d=>d.y)
            .context(context)
        : line()
            .x(d=>d.x)
            .y(d=>d.y)
            .context(context);
    interpolateCurve(opt.plots.curve, [curve]);

    context.beginPath();
    curve(particles);
    context.lineWidth = opt.plots.strokeWidth;
    gradientStroke(context, width, height, opt);

    context.stroke();
}

const euclideanDistance =(x1, y1, x2, y2)=> {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}

class Area extends AbstractBasicCartesianChartWithAxes {
    constructor(canvasId, _userOptions) {
        super(canvasId, _userOptions);
    }

    render(_data) {
        super.render(_data);
        // this._getMetric().scale.range([this._frontCanvas.node().height, 0]);
        // this._getDimension().scale.range([0, this._frontCanvas.node().width]);
        this._animate();
    }

    update() {
        super.update();
        this._getMetric().scale.range([this._frontCanvas.node().height, 0]);
        this._getDimension().scale.range([0, this._frontCanvas.node().width]);
        this._animate();
    }

    _animate() {
        const Duration = this._options.animation.duration.update;
        const stops = linearStops(this._options.color.scheme);
        const nodeColor = stops[stops.length - 1].color;

        const initialState = this.previousState
            ? this.previousState
            : this._data.map(d=>{
                return {
                    x: this._x(d),
                    y: this._frontCanvas.node().height,
                    r: this._options.plots.nodeRadius,
                    c: nodeColor,
                    alpha: 0
                }
        });

        const finalState = this._data.map(d=>{
            return {
                x: this._x(d),
                y: this._y(d),
                r: this._options.plots.nodeRadius,
                c: nodeColor,
                alpha: 1
            }
        });

        // cache finalState as the initial state of next animation call
        this.previousState = finalState;

        const interpolateParticles = interpolateArray(initialState, finalState);

        let that = this;
        const batchRendering = timer( (timeSinceStart)=> {
            let t = Math.min(timeSinceStart/Duration, 1);

            draw(that._frontContext,
                interpolateParticles(t),
                that._frontCanvas.node().width,
                that._frontCanvas.node().height,
                that._options);

            if (t === 1) {
                batchRendering.stop();
                that._voronoi = applyVoronoi(that._frontContext,
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, finalState);

                that._quadtree = applyQuadtree(that._frontContext,
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, finalState);

                /**
                 * callback for when the mouse moves across the overlay
                 */
                function mouseMoveHandler() {
                    console.log('------')
                    // get the current mouse position
                    const [mx, my] = mouse(this);
                    const QuadtreeRadius = 20;
                    // use the new diagram.find() function to find the Voronoi site
                    // closest to the mouse, limited by max distance voronoiRadius
                    const closest = that._quadtree.find(mx, my, QuadtreeRadius);

                    that._tooltip.style("left", closest[0] + "px")
                        .style("top", closest[1] + "px")
                        .html( that._getTooltipHTML(d));

                    that._tooltip.style("opacity", 1)
                }

                function mouseOutHandler() {
                    that._tooltip.style("opacity", 0)
                }

                console.log(that._frontCanvas)

                that._frontCanvas.on('mousemove', mouseMoveHandler);
                that._frontCanvas.on('mouseout', mouseOutHandler);
                that._frontCanvas.on('click', function(){
                    console.log('--')
                });

                // draw hidden in parallel;
                draw(that._hiddenContext,
                    interpolateParticles(t),
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, true);
            }
        });
    }

    transitionColor(color) {
        this._options.color = color;

        this.update();
    }

    sort(accessor, direction) {
        this._options.ordering = {
            accessor: accessor,
            direction: direction
        };

        this.update();
    }

    createOptions(_userOpt) {
        return createCartesianOpt(AreaOpt, _userOpt);
    };
}

export default Area;<commit_msg>refactor draw function<commit_after>import {
    area,
    line,
} from 'd3-shape';
import { interpolateArray } from 'd3-interpolate';
import { timer } from 'd3-timer';
import { mouse } from 'd3-selection';
import {
    uuid,
    linearStops
} from 'vizart-core';

import { AbstractBasicCartesianChartWithAxes } from '../../base';
import createCartesianOpt from '../../options/createCartesianOpt';
import interpolateCurve from '../../util/curve';
import applyQuadtree from './quadtree/apply';
import applyVoronoi from './voronoi';
import gradientStroke from './gradient-stroke';
import genColorByIndex from './generate-color';

const AreaOpt = {
    chart: {
        type: 'area_horizontal'
    },
    plots: {
        areaOpacity: 1,
        curve: 'linear',
        strokeWidth: 4,
        nodeRadius: 4,
        drawArea: true,
        showDots: true
    }
}

const nodeColor = opt=> {
    const stops = linearStops(opt.color.scheme);
    return stops[stops.length - 1].color;
}

const drawPoints = (context, particles, width, height, opt, hidden)=> {
    for (const [i, p] of particles.entries()) {
        context.beginPath();
        context.fillStyle = hidden === true? genColorByIndex(i) : nodeColor(opt);
        context.globalAlpha = p.alpha;
        context.arc(p.x, p.y, p.r, 0, 2 * Math.PI, false);
        context.fill();
    }
}

const drawLine = (context, particles, width, height, opt)=> {
    const curve = (opt.plots.drawArea === true)
        ? area()
            .x(d=>d.x)
            .y0(opt.chart.innerHeight)
            .y1(d=>d.y)
            .context(context)
        : line()
            .x(d=>d.x)
            .y(d=>d.y)
            .context(context);
    interpolateCurve(opt.plots.curve, [curve]);

    context.beginPath();
    curve(particles);
    context.lineWidth = opt.plots.strokeWidth;
    gradientStroke(context, width, height, opt);

    context.stroke();
}

/**
 * a particle contains x, y, r, c, alpha
 *
 * @param context
 * @param particles, particle colors may be defined in rgb string and thus cannot be recognized by
 * canvas. This is caused by d3's interpolation.
 */
const draw = (context, particles, width, height, opt, hidden = false)=> {
    context.clearRect(0, 0, width, height);

    if (hidden === true) {
        drawPoints(context, particles, width, height, opt, true);
    } else {
        if (opt.plots.showDots === true) {
            drawPoints(context, particles, width, height, opt, false);
        }

        drawLine(context, particles, width, height, opt)
    }
}

const euclideanDistance =(x1, y1, x2, y2)=> {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}

class Area extends AbstractBasicCartesianChartWithAxes {
    constructor(canvasId, _userOptions) {
        super(canvasId, _userOptions);
    }

    render(_data) {
        super.render(_data);
        // this._getMetric().scale.range([this._frontCanvas.node().height, 0]);
        // this._getDimension().scale.range([0, this._frontCanvas.node().width]);
        this._animate();
    }

    update() {
        super.update();
        this._getMetric().scale.range([this._frontCanvas.node().height, 0]);
        this._getDimension().scale.range([0, this._frontCanvas.node().width]);
        this._animate();
    }

    _animate() {
        const Duration = this._options.animation.duration.update;
        const stops = linearStops(this._options.color.scheme);
        const nodeColor = stops[stops.length - 1].color;

        const initialState = this.previousState
            ? this.previousState
            : this._data.map(d=>{
                return {
                    x: this._x(d),
                    y: this._frontCanvas.node().height,
                    r: this._options.plots.nodeRadius,
                    c: nodeColor,
                    alpha: 0
                }
        });

        const finalState = this._data.map(d=>{
            return {
                x: this._x(d),
                y: this._y(d),
                r: this._options.plots.nodeRadius,
                c: nodeColor,
                alpha: 1
            }
        });

        // cache finalState as the initial state of next animation call
        this.previousState = finalState;

        const interpolateParticles = interpolateArray(initialState, finalState);

        let that = this;
        const batchRendering = timer( (timeSinceStart)=> {
            let t = Math.min(timeSinceStart/Duration, 1);

            draw(that._frontContext,
                interpolateParticles(t),
                that._frontCanvas.node().width,
                that._frontCanvas.node().height,
                that._options);

            if (t === 1) {
                batchRendering.stop();
                that._voronoi = applyVoronoi(that._frontContext,
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, finalState);

                that._quadtree = applyQuadtree(that._frontContext,
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, finalState);

                /**
                 * callback for when the mouse moves across the overlay
                 */
                function mouseMoveHandler() {
                    console.log('------')
                    // get the current mouse position
                    const [mx, my] = mouse(this);
                    const QuadtreeRadius = 20;
                    // use the new diagram.find() function to find the Voronoi site
                    // closest to the mouse, limited by max distance voronoiRadius
                    const closest = that._quadtree.find(mx, my, QuadtreeRadius);

                    that._tooltip.style("left", closest[0] + "px")
                        .style("top", closest[1] + "px")
                        .html( that._getTooltipHTML(d));

                    that._tooltip.style("opacity", 1)
                }

                function mouseOutHandler() {
                    that._tooltip.style("opacity", 0)
                }

                console.log(that._frontCanvas)

                that._frontCanvas.on('mousemove', mouseMoveHandler);
                that._frontCanvas.on('mouseout', mouseOutHandler);
                that._frontCanvas.on('click', function(){
                    console.log('--')
                });

                // draw hidden in parallel;
                draw(that._hiddenContext,
                    interpolateParticles(t),
                    that._hiddenCanvas.node().width,
                    that._hiddenCanvas.node().height,
                    that._options, true);
            }
        });
    }

    transitionColor(color) {
        this._options.color = color;

        this.update();
    }

    sort(accessor, direction) {
        this._options.ordering = {
            accessor: accessor,
            direction: direction
        };

        this.update();
    }

    createOptions(_userOpt) {
        return createCartesianOpt(AreaOpt, _userOpt);
    };
}

export default Area;<|endoftext|>


<commit_before>/**
 * Created by Obscurity on 2017/1/21.
 */

$("#container").hshare({
    size: "large",
    shares: [{
        platform: "qzone",
        icon: "https://qzonestyle.gtimg.cn/ac/qzone_v5/app/app_share/qz_logo.png",
        text: "分享到QQ空间"
    }, {
        platform: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        platform: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        platform: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share08.png",
        text: "人人网"
    }, {
        platform: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        platform: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});

$("#container2").hshare({
    size: "medium",
    renderText: true,
    shares: [{
        platform: "qzone",
        text: "分享到QQ空间"
    }, {
        platform: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        platform: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        platform: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share09.png",
        text: "人人网"
    }, {
        platform: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        platform: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});

$("#container3").hshare({
    size: "small",
    renderText: true,
    shares: [{
        platform: "qzone",
        icon: "https://qzonestyle.gtimg.cn/ac/qzone_v5/app/app_share/qz_logo.png",
        text: "分享到QQ空间"
    }, {
        platform: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        platform: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        platform: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share10.png",
        text: "人人网"
    }, {
        platform: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        platform: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});<commit_msg>增加了一个demo<commit_after>/**
 * Created by Obscurity on 2017/1/21.
 */

$("#container").hshare({
    size: "large",
    platforms: [{
        name: "qzone",
        icon: "https://qzonestyle.gtimg.cn/ac/qzone_v5/app/app_share/qz_logo.png",
        text: "分享到QQ空间"
    }, {
        name: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        name: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        name: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share08.png",
        text: "人人网"
    }, {
        name: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        name: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});

$("#container2").hshare({
    size: "medium",
    platforms: [{
        name: "qzone",
        text: "分享到QQ空间"
    }, {
        name: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        name: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        name: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share09.png",
        text: "人人网"
    }, {
        name: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        name: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});

$("#container3").hshare({
    size: "small",
    platforms: [{
        name: "qzone",
        icon: "https://qzonestyle.gtimg.cn/ac/qzone_v5/app/app_share/qz_logo.png",
        text: "分享到QQ空间"
    }, {
        name: "qq",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "QQ好友"
    }, {
        name: "douban",
        icon: "https://img3.doubanio.com/pics/fw2douban_s.png",
        text: "豆瓣"
    }, {
        name: "renren",
        icon: "http://dev.renren.com/views/website/inc/images/share10.png",
        text: "人人网"
    }, {
        name: "sinaweibo",
        icon: "https://img.t.sinajs.cn/t4/appstyle/widget/images/shareButton/share_icon_mini.png?id=1375775002754",
        text: "新浪微博"
    }, {
        name: "wechat",
        icon: "https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png",
        text: "微信"
    }]
});<|endoftext|>