Source: v.roundspan/0.1/v.roundspan.js

;(function(define, _win) { 'use strict'; define( 'v.roundspan', [ 
    'v', 'underscore', 'v.geometry', 'v.log', 'v.basehandler'
    , 'zepto.extras' ], function( V, _, Geometry, Log, BaseHandler ){
    V = V || window.V || {};
    Geometry = Geometry || V.Geometry;
    BaseHandler = BaseHandler || V.BaseHandler;
    Log = Log || V.Log;

    _ = _ || window._;

    /**
     * 圆环进度控制(多条件)
     * @module v.roundspan
     * @implements {@link v.module:basehandler}
     * @inheritdoc
     * @requires {@link v}
     * @requires {@link underscore}
     * @requires {@link v.module:geometry}
     * @requires {@link v.module:log}
     * @requires {@link Zepto#zepto.extras}
     * @export V.Const.RoundSpan
     * @see {@link http://demo.v.js.openjavascript.org/modules/v.roundspan/0.1/_demo/default_order.html demo}
     * @example
<link href='../res/default/style.css' rel='stylesheet' />
<div style="margin-top: 20px; margin-left: 150px; ">
    <div class="comp-rs" 
        data-comp="RoundSpan"
        data-offset_angle="210"
        data-max_angle="240"
        data-value="11,29"
        data-min_value="6"
        data-max_value="34"
        data-order="1"
        >
        <div class="comp-rs-control" data-control="point" ><label data-type="text"></label></div>
        <div class="comp-rs-control" data-control="point" ><label data-type="text"></label></div>
    </div>
</div>
<script>
    requirejs( [ 'v.roundspan' ], function( RoundSpan ){
        RoundSpan.trigger( RoundSpan.init );
    });
</script>
     */
    var _PREFIX = 'compRoundSpan_'
        , _CONST = $.extend( true, {
            init: _PREFIX + 'init'
            , initItem: _PREFIX + 'initItem'
            , getParams: _PREFIX + 'getParams'
            , updateParams: _PREFIX + 'updateParams'

            , precision: 0
            , checkorder: _PREFIX + 'checkorder'

            , calcCenterPoint: _PREFIX + 'calcCenterPoint'
            , dataKey: _PREFIX + 'data'
            , updateWrapperCor: _PREFIX + 'updateWrapperCor'
            /**
             * roundspan更新操作的事件名集合
             * @namespace   v.module:roundspan#update
             * @see {@link v.module:roundspan}
             */
            , update: {
                angle: _PREFIX + 'updateAngle'
                , visibleAngle: _PREFIX + 'updateVisibleAngle'

                , noticeStart: _PREFIX + 'updateNoticeStart'
                , notice: _PREFIX + 'updateNotice'
                , noticeEnd: _PREFIX + 'updateNoticeEnd'

                , values: _PREFIX + 'update_values'
                , value: _PREFIX + 'update_value'
                , text: _PREFIX + 'updateNoticeText'
            }
            , calcVisibleParams: _PREFIX + 'calcVisibleParams'


            , pointDelegate: [
                'div[data-comp=RoundSpan] [data-control=point]'
                , 'li[data-comp=RoundSpan] [data-control=point]'
            ].join(',')
            , wrapperSelector: '[data-comp=RoundSpan]' 

            /**
             * 圆点拖动时检测是否可以拖动
             * @event v.module:roundspan#beforeDragCheckEvent
             * @return {boolean}
             * @example 
 <div class="temp-circle isRoomItem" data-comp="RoundSpan" data-offset_angle="210" data-max_angle="240" data-value="17,24" data-min_value="5" data-max_value="35" data-room_num="3" data-order="1" data-before_drag_check_event="customEvent">
     <span class="temp-high" data-control="point" data-type="lower" data-point_value="17" style="z-index: 101; left: 10.9731px; top: -13.8172px;">
         <label data-type="text">17</label>
     </span>
     <span class="temp-low" data-control="point" data-type="upper" data-point_value="24" style="z-index: 102; left: 50.8223px; top: -10.3988px;">
         <label data-type="text">24</label>
     </span>
     <div class="temp-hidden"></div>
 </div>
 <script>
    V.WIN.on( 'customEvent', function( _evt, _src, _params ){
        return window.isCanDrag;
    });
 </script>
             */
            , beforeDragCheckEvent: 'before_drag_check_event'
        }, BaseHandler )
        , _PARAMS
        , _LAST_ANGLE = 0
        ;
    V.Const.RoundSpan = _CONST;
    /**
     * 初始化圆环
     * @event v.module:roundspan#init
     * @param {selector} _wrapper   要初始化的复选框
     * @param {boolean}  _isItem 声明 _wrapper 是否为单个组件, 否则初始化 _wrapper 下的所有 RoundSpan, default: false
     * @example  
     * RoundSpan.trigger( RoundSpan.init, [ 'body' ]  )
     * //RoundSpan.trigger( RoundSpan.init, [ $( 'div[data-comp=RoundSpan]', true ) ]  )
     */
    _CONST.on( _CONST.init, function( _evt, _wrapper, _isItem ){
        if( _isItem ){
            $.each( _wrapper, function( _k, _v ){
                _CONST.trigger( _CONST.initItem, [ $( _v ) ] );
            });
        }else{
            _wrapper = $( _wrapper || V.BODY );
            _wrapper.length && 
            _wrapper.find( 'div[data-comp=RoundSpan]' ).each( function( _k, _v ){
                _CONST.trigger( _CONST.initItem, [ _v ] );
            });
        }
    });
    /**
     * 初始化单个圆环
     * @event v.module:roundspan#initItem
     * @param {selector} _item  要初始化的圆环
     * @example  
     * RoundSpan.trigger( RoundSpan.initItem, [ 'div[data-comp=RoundSpan]:first' ]  )
     */
    _CONST.on( _CONST.initItem, function( _evt, _item ){
        _item = $( _item );
        if( !_item.length ) return;

        var _val = (_item.data( 'value' ) || '0' ).toString().replace( /[^\d,]/g, '' ).split( ',' )
            , _points = _item.find( '[data-control=point]' )
            ;

        $.each( _points, function( _k, _v ){
            _v = $( _v );
            var _prev = _points[ _k - 1]
                , _next = _points[ _k + 1]
                ;

            _v[0].prev = _prev;
            _v[0].next = _next;
        });
        

        //Log.dir( _params, _val );
        //
        _points.each( function( _k, _v ){
            var _point = $( _v )
                , _params = _CONST.triggerHandler( _CONST.getParams, [ _point ] )
                , _sv = _val[ _k ] || _params.min_value
                ;
            _CONST.trigger( _CONST.updateWrapperCor, [ _params ] ); 
            var _angle = ( _sv - _params.min_value ) / _params.value * _params.max_angle - _params.offsetAngle;
            _CONST.trigger( _CONST.update.angle, [ _angle, _params, true ] );
        });

    });
    /**
     * 检查圆环是否需要进行大小比较
     * @event v.module:roundspan#checkorder
     * @param {object} _params          RoundSpan 的数据模型
     * @param {object} _visibleParams   RoundSpan 的可视化数据模型
     * @return {boolean}
     * @private
     */
    _CONST.on( _CONST.checkorder, function( _evt, _params, _visibleParams ){
        var _r = true;

        if( !_params.order ) return _r;
        var _point = _params.point  
            , _prev = _point[0].prev
            , _next = _point[0].next
            ;
        //Log.dir( _params );
        if( _prev ){
            if( _visibleParams.value <= $( _prev ).data( 'point_value' ) ) _r = false;
        }
        if( _next ){
            if( _visibleParams.value >= $( _next ).data( 'point_value' ) ) _r = false;
        }

        return _r;
    });
    _CONST.on( _CONST.calcVisibleParams, function( _evt, _angle, _params ){
        var _r = { 
            angle: _angle
            , visibleAngle: 0
            , value: 0
        };

        _r.visibleAngle = Geometry.fixAngle( _r.angle + _params.offsetAngle );

        if( _params.max_angle && _r.visibleAngle > _params.max_angle ){
            if( _r.visibleAngle > 20 && _r.visibleAngle <= ( _params.max_angle + ( 360 - _params.max_angle ) / 2 )){
                _r.angle -= ( _r.visibleAngle - _params.max_angle );
                _r.visibleAngle = _params.max_angle;
            }else{
                _r.angle = -_params.offsetAngle;
                _r.visibleAngle = 0;
            }
        }
        //Log.log( _params.max_angle, _r.visibleAngle, _params.value, _.now() );
        _r.value = V.utils.parseFinance(  _r.visibleAngle / _params.max_angle * _params.value + _params.min_value, _CONST.precision );

        return _r;
    });

    _CONST.on( _CONST.updateWrapperCor, function( _evt, _params ){
        var _wp = _params.wrapper
            , _pt = _params.point
            , _position = _wp.position()
            , _offset = _wp.offset()
            , _ptposition = _pt.position()
            , _ptoffset = _pt.offset()
            ;

        _params.cor = {
            left: _position.left
            , top: _position.top
            , offsetLeft: _offset.left
            , offsetTop: _offset.top
            , width: _wp.width()
            , height: _wp.height()
            , offsetWidth: _wp[0].offsetWidth
            , offsetHeight: _wp[0].offsetHeight
        };
        _CONST.triggerHandler( _CONST.calcCenterPoint, _params.cor );

        _params.pcor = {
            left: _ptposition.left
            , top: _ptposition.top
            , offsetLeft: _ptoffset.left
            , offsetTop: _ptoffset.top
            , width: _pt.outerWidth( true )
            , height: _pt.outerHeight( true )
            , offsetWidth: _pt.outerWidth( true ) || _pt[0].offsetWidth
            , offsetHeight: _pt.outerHeight( true ) || _pt[0].offsetHeight
        };
        _CONST.triggerHandler( _CONST.calcCenterPoint, _params.pcor );

        _CONST.trigger( _CONST.updateParams, [ _params ]  );
    });
    _CONST.on( _CONST.calcCenterPoint, function( _evt, _cor ){
        var _r = { 
                cx: _cor.offsetLeft + _cor.offsetWidth / 2 
                , cy: _cor.offsetTop + _cor.offsetHeight / 2
                , radius: Math.max( _cor.offsetWidth, _cor.offsetHeight ) / 2
            };
        $.extend( _cor, _r );

        return _r;
    });
    _CONST.on( _CONST.updateParams, function( _evt, _params ){
        _params.wrapper[0][ _CONST.dataKey ] = _params;
        return _params;
    });
    _CONST.on( _CONST.getParams, function( _evt, _p ){
        _p = $( _p );
        var _r = _p[0][ _CONST.dataKey ];

        if( !_r ){
            _r = {
                wrapper: _p.closest( _CONST.wrapperSelector )
                , point: _p
                , points: null
                , text: _p.find( '[data-type=text]' )
                , offsetAngle: 90
                , drag_check_event: ''
                , max_angle: 360
                , min_value: 0
                , max_value: 0
                , value: 0
            }
            _r.points = _r.wrapper.find( '[data-control=point]' );
            _r.offsetAngle = _r.wrapper.data( 'offset_angle' ) || _r.offsetAngle
            _r.max_angle = _r.wrapper.data( 'max_angle' ) || _r.max_angle

            _r.drag_check_event = _r.wrapper.data( 'drag_check_event' ) || _r.drag_check_event
            _p[0][ _CONST.dataKey ] = _r;

            //Log.log( _r.wrapper.data( 'max_value' ), _.now() );

            _r.min_value = _r.wrapper.data( 'min_value' ) || _r.min_value;
            _r.max_value = _r.wrapper.data( 'max_value' ) || _r.max_angle;
            _r.value = _r.max_value - _r.min_value;

            _r.order = V.utils.parseBool( _r.wrapper.data( 'order' ) );
        }
        return _r;
    });
    /**
     * 更新圆环单个点的文本
     * @event text
     * @param {number} _visibleAngle    可视化角度
     * @param {object} _params          RoundSpan 的数据模型
     * @param {number} _angle           程序角度
     * @param {number} _value           用于显示的文本数值
     * @memberof v.module:roundspan#update
     * @see {@link v.module:roundspan}
     */
    _CONST.on( _CONST.update.text, function( _evt, _visibleAngle, _params, _angle, _value ){
        if ( !( _params.text && _params.text.length ) ) return;
        _params.text.html( Math.ceil( _value ) );
    });
    /**
     * 更新圆环的所有点
     * @event values
     * @param {object} _params      RoundSpan 的数据模型
     * @memberof v.module:roundspan#update
     * @see {@link v.module:roundspan}
     */
    _CONST.on( _CONST.update.values, function( _evt, _params ){
        var _vals = [];
        _params.points.each( function( _k, _v ){
            _v = $( _v );
            var _label = _v.find( '[data-type=text]' ), _tmp;
            _label.length && ( _tmp = _label.html().replace( /[^\d.]/g, '' ) ).length &&  _vals.push( V.utils.parseFinance( _tmp ) );
        });
        _vals.length && ( _vals = _.sortBy( _vals ) ) && ( 
            _params.wrapper.data( 'value', _vals.join( ',' ) ) 
            , _params.wrapper[0].RoundSpanData = _vals
            , _CONST.trigger( _CONST.update.value, [ _vals, _params ] )
        );
    });
    /**
     * 更新圆环的角度
     * @event angle
     * @param {number}  _angle          角度
     * @param {object}  _params         RoundSpan 的数据模型
     * @param {boolean} _ignoreNotice   是否忽略更新后的通知事件, default: false
     * @memberof v.module:roundspan#update
     * @see {@link v.module:roundspan}
     */
    _CONST.on( _CONST.update.angle, function( _evt, _angle, _params, _ignoreNotice ){
        _params = _params || _PARAMS;
        if( !_params ) return;
        var _visibleParams = _CONST.triggerHandler( _CONST.calcVisibleParams, [ _angle, _params ] );

        if( !_ignoreNotice ){
            if( !_CONST.triggerHandler( _CONST.checkorder, [ _params, _visibleParams ] ) ){
                return;
            }
        }

        _params.point.data( 'point_value', _visibleParams.value );

        var _pt = { 
                left: _params.cor.radius - _params.pcor.radius - 2
                , top: _params.cor.radius - _params.pcor.radius - 2
            }
            , _realPt = Geometry.distanceAngleToPoint( _params.cor.radius, _visibleParams.angle )
            ;

        _params.point.css( { left: _realPt.x + _pt.left, top: _realPt.y + _pt.top } );

        _CONST.trigger( _CONST.update.text, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] ); 

        _CONST.trigger( _CONST.update.values, [ _params ] );

        !_ignoreNotice 
            && _CONST.trigger( _CONST.update.notice, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );
    });

    V.DOC.delegate( _CONST.pointDelegate, 'touchend', function( _evt ){
        var _params =  _CONST.triggerHandler( _CONST.getParams, [ this ] )
            ;
        V.DOC.off( 'touchmove', onTouchMove );
        V.Log.log( 'touchend', _params.wrapper.length, _.now(), _LAST_ANGLE );

        var _visibleParams = _CONST.triggerHandler( _CONST.calcVisibleParams, [ _LAST_ANGLE, _params ] )

        _CONST.trigger( _CONST.update.values, [ _params ] );

        _CONST.trigger( _CONST.update.noticeEnd, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );

        _PARAMS = null;
    });

    V.DOC.delegate( _CONST.pointDelegate, 'touchstart', function( _evt ){
        _evt.preventDefault();
        _evt.stopPropagation();
        var _p = $( this )
            , _checkEvent
            ;


        var _params =  _CONST.triggerHandler( _CONST.getParams, [ this ] )
            ;
            
        _checkEvent = V.utils.detectCommand( _params.wrapper.data( _CONST.beforeDragCheckEvent ) );
        if( _checkEvent 
            && 
            ( 
                ( !V.WIN.triggerHandler( _checkEvent, [ _p, _params ] ) )
                //|| ( !_CONST.triggerHandler( _checkEvent, [ _p, _params ] ) )
            )
        ) return;

        _p.css( 'z-index', ++V.ZINDEX );
        
        _CONST.trigger( _CONST.updateWrapperCor, [ _params ] ); 
        _PARAMS = _params;

        var _angle = 
                Geometry.lineAngle( 
                    { x: _PARAMS.cor.cx, y: _PARAMS.cor.cy }
                    , { x: _evt.touches[0].pageX, y: _evt.touches[0].pageY } 
                );

        var _visibleParams = _CONST.triggerHandler( _CONST.calcVisibleParams, [ _angle, _params ] )
        _CONST.trigger( _CONST.update.noticeStart, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );

        //V.Log.dir( _params );
        V.Log.log( 'touchstart', _params.wrapper.length, _.now() );
        V.DOC.off( 'touchmove', onTouchMove ).on( 'touchmove', onTouchMove );
    });


    function onTouchMove( _evt ){
        if( !_PARAMS ) return;
        //V.Log.dir( _evt, 'onTouchMove' );
        //V.Log.log( _.now(), _evt.touches[0].pageX, _evt.touches[0].pageY );

        var _angle = 
                Geometry.lineAngle( 
                    { x: _PARAMS.cor.cx, y: _PARAMS.cor.cy }
                    , { x: _evt.touches[0].pageX, y: _evt.touches[0].pageY } 
                );
        _LAST_ANGLE = _angle;

        _CONST.trigger( _CONST.update.angle, [ _angle ] );
    }

    _CONST.on( _CONST.update.noticeEnd, function( _evt, _visibleAngle, _params, _angel, _value, _visibleParams ){
        V.WIN.trigger( _CONST.update.noticeEnd, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );
    });
    _CONST.on( _CONST.update.notice, function( _evt, _visibleAngle, _params, _angel, _value, _visibleParams ){
        V.WIN.trigger( _CONST.update.notice, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );
    });
    _CONST.on( _CONST.update.notice, function( _evt, _visibleAngle, _params, _angel, _value, _visibleParams ){
        V.WIN.trigger( _CONST.update.noticeStart, [ _visibleParams.visibleAngle, _params, _visibleParams.angle, _visibleParams.value, _visibleParams ] );
    });

    //bind on V.WIN no longer support

    V.WIN.on( _CONST.update.values, function( _evt, _params ){
        _CONST.trigger( _CONST.update.values, [ _params ] );
    });

    V.WIN.on( _CONST.update.angle, function( _evt, _angle, _params, _ignoreNotice ){
        _CONST.trigger( _CONST.update.angle, [ _angle, _params, _ignoreNotice ] );
    });

    V.WIN.on( _CONST.checkorder, function( _evt, _params, _visibleParams ){
        return _CONST.triggerHandler( _CONST.checkorder, [ _params, _visibleParams ] );
    });

    V.WIN.on( _CONST.init, function( _evt, _wrapper, _isItem ){
        _CONST.trigger( _CONST.init, [ _wrapper, _isItem ] );
    });

    V.WIN.on( _CONST.initItem, function( _evt, _item ){
        _CONST.trigger( _CONST.initItem, [ _item ] );
    });

    V.WIN.on( _CONST.update.text, function( _evt, _visibleAngle, _params, _angle, _value ){
        _CONST.trigger( _CONST.update.text, [ _visibleAngle, _params, _angle, _value ] );
    });

    V.WIN.on( _CONST.calcVisibleParams, function( _evt, _angle, _params ){
        return _CONST.triggerHandler( _CONST.calcVisibleParams, [ _angle, _params ] )
    });

    V.WIN.on( _CONST.updateWrapperCor, function( _evt, _params ){
        _CONST.trigger( _CONST.updateWrapperCor, [ _evt, _params ] );
    });

    V.WIN.on( _CONST.calcCenterPoint, function( _evt, _cor ){
        return _CONST.triggerHandler( _CONST.calcCenterPoint, [ _cor ] );
    });

    V.WIN.on( _CONST.updateParams, function( _evt, _params ){
        _CONST.trigger( _CONST.updateParams, [ _params ] );
    });

    V.WIN.on( _CONST.getParams, function( _evt, _p ){
        return _CONST.triggerHandler( _CONST.getParams, [ _p ] );
    });

    return _CONST;

});}( typeof define === 'function' && define.amd ? define : 
        function ( _name, _require, _cb ) { 
            typeof _name == 'function' && ( _cb = _name );
            typeof _require == 'function' && ( _cb = _require ); 
            _cb && ( _cb() ); 
        }
        , window
    )
);