<commit_before>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var annAttrs = require('../../components/annotations/attributes');
var extendFlat = require('../../lib/extend').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;

module.exports = overrideAll({
    domain: {
        x: {
            valType: 'info_array',
            role: 'info',
            items: [
                {valType: 'number', min: 0, max: 1},
                {valType: 'number', min: 0, max: 1}
            ],
            dflt: [0, 1],
            description: [
                'Sets the horizontal domain of this `table` trace',
                '(in plot fraction).'
            ].join(' ')
        },
        y: {
            valType: 'info_array',
            role: 'info',
            items: [
                {valType: 'number', min: 0, max: 1},
                {valType: 'number', min: 0, max: 1}
            ],
            dflt: [0, 1],
            description: [
                'Sets the vertical domain of this `table` trace',
                '(in plot fraction).'
            ].join(' ')
        }
    },

    columnwidth: {
        valType: 'number',
        arrayOk: true,
        dflt: null,
        role: 'style',
        description: 'The width of cells.'
    },

    columnorder: {
        valType: 'data_array',
        role: 'info',
        description: [
            'Specifies the rendered order of the data columns; for example, a value `2` at position `0`',
            'means that column index `0` in the data will be rendered as the',
            'third column, as columns have an index base of zero.'
        ].join(' ')
    },

    header: {

        values: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Header cell values. `values[m][n]` represents the value of the `n`th point in column `m`,',
                'therefore the `values[m]` vector length for all columns must be the same (longer vectors',
                'will be truncated). Each value must be a finite number or a string.'
            ].join(' ')
        },

        format: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Sets the cell value formatting rule using d3 formatting mini-language',
                'which is similar to those of Python. See',
                'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
            ].join(' ')
        },

        prefix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Prefix for cell values.'
        },

        suffix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Suffix for cell values.'
        },

        height: {
            valType: 'number',
            dflt: 28,
            role: 'style',
            description: 'The height of cells.'
        },

        align: extendFlat({}, annAttrs.align, {arrayOk: true}),

        line: {
            width: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        },

        fill: {
            color: {
                valType: 'color',
                arrayOk: true,
                dflt: 'white',
                role: 'style',
                description: [
                    'Sets the cell fill color. It accepts either a specific color',
                    ' or an array of colors.'
                ].join('')
            }
        },

        font: {
            family: {
                valType: 'string',
                arrayOk: true,
                role: 'style',
                noBlank: true,
                strict: true,
                description: [
                    'HTML font family - the typeface that will be applied by the web browser.',
                    'The web browser will only be able to apply a font if it is available on the system',
                    'which it operates. Provide multiple font families, separated by commas, to indicate',
                    'the preference in which to apply fonts if they aren\'t available on the system.',
                    'The plotly service (at https://plot.ly or on-premise) generates images on a server,',
                    'where only a select number of',
                    'fonts are installed and supported.',
                    'These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*,',
                    '*Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*,',
                    '*PT Sans Narrow*, *Raleway*, *Times New Roman*.'
                ].join(' ')
            },
            size: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        }
    },

    cells: {

        values: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Cell values. `values[m][n]` represents the value of the `n`th point in column `m`,',
                'therefore the `values[m]` vector length for all columns must be the same (longer vectors',
                'will be truncated). Each value must be a finite number or a string.'
            ].join(' ')
        },

        format: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Sets the cell value formatting rule using d3 formatting mini-language',
                'which is similar to those of Python. See',
                'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
            ].join(' ')
        },

        prefix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Prefix for cell values.'
        },

        suffix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Suffix for cell values.'
        },

        height: {
            valType: 'number',
            dflt: 20,
            role: 'style',
            description: 'The height of cells.'
        },

        align: extendFlat({}, annAttrs.align, {arrayOk: true}),

        line: {
            width: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        },

        fill: {
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style',
                dflt: 'white',
                description: [
                    'Sets the cell fill color. It accepts either a specific color',
                    ' or an array of colors.'
                ].join('')
            }
        },

        font: {
            family: {
                valType: 'string',
                arrayOk: true,
                role: 'style',
                noBlank: true,
                strict: true,
                description: [
                    'HTML font family - the typeface that will be applied by the web browser.',
                    'The web browser will only be able to apply a font if it is available on the system',
                    'which it operates. Provide multiple font families, separated by commas, to indicate',
                    'the preference in which to apply fonts if they aren\'t available on the system.',
                    'The plotly service (at https://plot.ly or on-premise) generates images on a server,',
                    'where only a select number of',
                    'fonts are installed and supported.',
                    'These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*,',
                    '*Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*,',
                    '*PT Sans Narrow*, *Raleway*, *Times New Roman*.'
                ].join(' ')
            },
            size: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        }
    }
}, 'calc', 'from-root');
<commit_msg>reuse font_attributes<commit_after>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var annAttrs = require('../../components/annotations/attributes');
var extendFlat = require('../../lib/extend').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;
var fontAttrs = require('../../plots/font_attributes');

module.exports = overrideAll({
    domain: {
        x: {
            valType: 'info_array',
            role: 'info',
            items: [
                {valType: 'number', min: 0, max: 1},
                {valType: 'number', min: 0, max: 1}
            ],
            dflt: [0, 1],
            description: [
                'Sets the horizontal domain of this `table` trace',
                '(in plot fraction).'
            ].join(' ')
        },
        y: {
            valType: 'info_array',
            role: 'info',
            items: [
                {valType: 'number', min: 0, max: 1},
                {valType: 'number', min: 0, max: 1}
            ],
            dflt: [0, 1],
            description: [
                'Sets the vertical domain of this `table` trace',
                '(in plot fraction).'
            ].join(' ')
        }
    },

    columnwidth: {
        valType: 'number',
        arrayOk: true,
        dflt: null,
        role: 'style',
        description: 'The width of cells.'
    },

    columnorder: {
        valType: 'data_array',
        role: 'info',
        description: [
            'Specifies the rendered order of the data columns; for example, a value `2` at position `0`',
            'means that column index `0` in the data will be rendered as the',
            'third column, as columns have an index base of zero.'
        ].join(' ')
    },

    header: {

        values: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Header cell values. `values[m][n]` represents the value of the `n`th point in column `m`,',
                'therefore the `values[m]` vector length for all columns must be the same (longer vectors',
                'will be truncated). Each value must be a finite number or a string.'
            ].join(' ')
        },

        format: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Sets the cell value formatting rule using d3 formatting mini-language',
                'which is similar to those of Python. See',
                'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
            ].join(' ')
        },

        prefix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Prefix for cell values.'
        },

        suffix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Suffix for cell values.'
        },

        height: {
            valType: 'number',
            dflt: 28,
            role: 'style',
            description: 'The height of cells.'
        },

        align: extendFlat({}, annAttrs.align, {arrayOk: true}),

        line: {
            width: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        },

        fill: {
            color: {
                valType: 'color',
                arrayOk: true,
                dflt: 'white',
                role: 'style',
                description: [
                    'Sets the cell fill color. It accepts either a specific color',
                    ' or an array of colors.'
                ].join('')
            }
        },

        font: extendFlat({}, fontAttrs({arrayOk: true}))
    },

    cells: {

        values: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Cell values. `values[m][n]` represents the value of the `n`th point in column `m`,',
                'therefore the `values[m]` vector length for all columns must be the same (longer vectors',
                'will be truncated). Each value must be a finite number or a string.'
            ].join(' ')
        },

        format: {
            valType: 'data_array',
            role: 'info',
            dflt: [],
            description: [
                'Sets the cell value formatting rule using d3 formatting mini-language',
                'which is similar to those of Python. See',
                'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
            ].join(' ')
        },

        prefix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Prefix for cell values.'
        },

        suffix: {
            valType: 'string',
            arrayOk: true,
            dflt: null,
            role: 'style',
            description: 'Suffix for cell values.'
        },

        height: {
            valType: 'number',
            dflt: 20,
            role: 'style',
            description: 'The height of cells.'
        },

        align: extendFlat({}, annAttrs.align, {arrayOk: true}),

        line: {
            width: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        },

        fill: {
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style',
                dflt: 'white',
                description: [
                    'Sets the cell fill color. It accepts either a specific color',
                    ' or an array of colors.'
                ].join('')
            }
        },

        font: {
            family: {
                valType: 'string',
                arrayOk: true,
                role: 'style',
                noBlank: true,
                strict: true,
                description: [
                    'HTML font family - the typeface that will be applied by the web browser.',
                    'The web browser will only be able to apply a font if it is available on the system',
                    'which it operates. Provide multiple font families, separated by commas, to indicate',
                    'the preference in which to apply fonts if they aren\'t available on the system.',
                    'The plotly service (at https://plot.ly or on-premise) generates images on a server,',
                    'where only a select number of',
                    'fonts are installed and supported.',
                    'These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*,',
                    '*Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*,',
                    '*PT Sans Narrow*, *Raleway*, *Times New Roman*.'
                ].join(' ')
            },
            size: {
                valType: 'number',
                arrayOk: true,
                role: 'style'
            },
            color: {
                valType: 'color',
                arrayOk: true,
                role: 'style'
            }
        }
    }
}, 'calc', 'from-root');
<|endoftext|>


<commit_before>angular.module('people.ctrl', [])

.controller('PeopleListCtrl',
  function($scope, $ionicLoading, $ionicScrollDelegate, $ionicPopup, $ionicHistory,
    PeopleListQuery, PeopleFilterModel, PersonalInfo) {

  //从登录跳转，则清空跳转历史
  // var history = $ionicHistory.viewHistory();
  // if (history.backView.url === '/login') {
  //   $ionicHistory.clearHistory()
  // }

  $scope.$emit('load.people.list');

  $scope.list = [];
  $scope.hasMore = true;

  $scope.loadMore = function() {

    var params = PeopleFilterModel.params();

    // 显示 loading 动画
    $ionicLoading.show({
      templateUrl: 'templates/people/people-maching.html'
    });

    // 从服务器加载数据，包括初次加载、加载更多，以及重新筛选
    PeopleListQuery.get(params, function(response) {

      console.log('request', params);

      var data;

      // console.log(response);

      if (response.errno === 0) {

        var data = response.data;

        if (data.length > 0) {
          // 追加室友列表并更新页数
          if (params.p === 1) {
            $ionicScrollDelegate.scrollTop();
          }
          $scope.list = params.p === 1 ? data : $scope.list.concat(response.data);
          PeopleFilterModel.increasePage();
        } else {

          console.log('no more');
          $scope.hasMore = false;
          // PeopleFilterModel.setMore(false);
        }

        PeopleFilterModel.setUsingCache(true);
      } else {

        // TODO error handling
        $scope.hasMore = true;
        // PeopleFilterModel.setMore(true);
      }

      // 关闭 loading 动画
      $ionicLoading.hide();
      $scope.$broadcast('scroll.infiniteScrollComplete');

    }, function(err) {

      console.log('err', err);

      $ionicLoading.hide();
      // PeopleFilterModel.setMore(false);
      $scope.hasMore = false;
      $scope.$broadcast('scroll.infiniteScrollComplete');
    });
  };

  $scope.$on('$stateChangeSuccess', function(event, toState) {
    if (toState.name === 'menu.people-list' && !PeopleFilterModel.isUsingCache()) {
      $scope.loadMore();
      PeopleFilterModel.setUsingCache(true);
    }
  });

  // 在跳转到室友详情之前，先判断是否填完个人信息
  $scope.jumpToDetail = function(hash) {

    // console.log(PersonalInfo);
    // PersonalInfo.completeInfo = false;
    // PersonalInfo.completeAsk = true;

    // TODO 判断是否完成问卷
    if (PersonalInfo.completeInfo && PersonalInfo.completeAsk) {
      $scope.go(hash);
    } else if (!PersonalInfo.completeAsk && !PersonalInfo.completeInfo) {
      $ionicPopup.confirm({
        template: '只有填写自己的个人信息和匹配问题才能为您匹配室友，并查看详情信息哟。',
        okText: '现在填写',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me-register');
        } else {
          // do nothing
        }
      });
    } else if (!PersonalInfo.completeInfo) {
      $ionicPopup.confirm({
        template: '只有填写自己的个人信息才能看到室友的详情信息哟。',
        okText: '现在填写',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me-register');
        } else {
          // do nothing
        }
      });
    } else if (!PersonalInfo.completeAsk) {
      $ionicPopup.confirm({
        template: '请回答以下7个问题，以便为您更精确匹配室友。',
        okText: '现在回答',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me/q/1');
        } else {
          // do nothing
        }
      });
    }
  };
})

.controller('PeopleFilterCtrl', function($scope, $state, PeopleFilterModel) {
  $scope.buttons = PeopleFilterModel.radio;
  $scope.list = PeopleFilterModel.list;
  $scope.params = PeopleFilterModel.params();

  $scope.finish = function() {
    PeopleFilterModel.resetPage();
    PeopleFilterModel.setMore(true);
    PeopleFilterModel.setUsingCache(false);
    $state.go('menu.people-list');
  };

  $scope.back = function() {
    PeopleFilterModel.setUsingCache(true);
    $state.go('menu.people-list');
  };
})

.controller('PeopleDetailWrapperCtrl',
  function($scope, $ionicActionSheet, $stateParams, PersonalInfo, $ionicPopup,
    PeopleDetailQuery, FavAdd, FavRemove, ForbidAdd) {

  PeopleDetailQuery.get({id: $stateParams.id}, function(response) {
    console.log('detail', response);

    if (response.errno === 0) {
      $scope.people = response.data;
      $scope.house = $scope.people.matchUserHouse;
      // console.log($scope.house);
    }
  }, function(err) {
    console.log('error', err);
  });

  // 显示 收藏/屏幕 菜单
  $scope.showMenu = function() {

    // 返回一个关闭菜单的函数
    $ionicActionSheet.show({
      buttons: [
        { text: $scope.people.isFav ? '取消收藏' : '收藏' },
        { text: '不喜欢' }
      ],
      cancelText: '取消',
      buttonClicked: function(index) {

        if (index === 0) {

          // 处理添加收藏或者取消收藏
          console.log('fav');

          if ($scope.people.isFav) {

            // 取消收藏
            FavRemove.save({userId: PersonalInfo.userId, favId: $scope.people.userId},
              function(response) {
                if (response.errno === 0) {
                  $scope.people.isFav = false;
                }
              }, function(err) {
                console.log('err', err);
              });

          } else {

            // 添加收藏
            FavAdd.save({userId: PersonalInfo.userId, favId: $scope.people.userId},
              function(response) {
                if (response.errno === 0) {
                  $scope.people.isFav = true;
                }
              }, function(err) {
                console.log('err', err);
              });
          }
        } else if (index === 1) {

          // 处理屏蔽
          console.log('forbid');

          ForbidAdd.save({userId: PersonalInfo.userId, forbidId: $scope.people.userId},
            function(response) {
              if (response.errno === 0) {
                console.log('forbid success');
              }
            }, function(err) {
              console.log('err', err);
            });
        }

        return true;
      }
    });
  };

  $scope.sendMessage = function() {

    if (SMS) {
      $ionicPopup.prompt({
        title: '短信',
        template: '请输入短信内容',
        inputPlaceholder: '短信内容'
      }).then(function(res) {
        console.log(res);

        if (res || res.trim().length == 0) return;

        SMS.sendSMS($scope.people.tel, res, function() {
          // console.log('success');
          $ionicPopup.alert({
            template: '发送成功'
          });
        }, function(){
          // console('failure');
          $ionicPopup.alert({
            template: '发送失败'
          });
        });
      });
     }
  };
})

.controller('PeopleDetailInfoCtrl', function($scope) {
  $scope.showInfo = true;
  console.log($scope.people);
})

.controller('PeopleDetailHouseCtrl', function($scope) {
  $scope.showHouse = true;
  $scope.log($scope.house);
})

.controller('FavCtrl', function($scope, PersonalInfo, FavQuery) {

  FavQuery.get({userId: PersonalInfo.userId}, function(response) {
    console.log('fav list', response);

    if (response.errno === 0) {
      $scope.list = response.data;
    }
  }, function(err) {
    console.log('err', err);
  });

  // 可以直接查看详情，不需要其他权限
  $scope.jumpToDetail = $scope.go;
})
;<commit_msg>修复无法查看室友房源的错误<commit_after>angular.module('people.ctrl', [])

.controller('PeopleListCtrl',
  function($scope, $ionicLoading, $ionicScrollDelegate, $ionicPopup, $ionicHistory,
    PeopleListQuery, PeopleFilterModel, PersonalInfo) {

  //从登录跳转，则清空跳转历史
  // var history = $ionicHistory.viewHistory();
  // if (history.backView.url === '/login') {
  //   $ionicHistory.clearHistory()
  // }

  $scope.$emit('load.people.list');

  $scope.list = [];
  $scope.hasMore = true;

  $scope.loadMore = function() {

    var params = PeopleFilterModel.params();

    // 显示 loading 动画
    $ionicLoading.show({
      templateUrl: 'templates/people/people-maching.html'
    });

    // 从服务器加载数据，包括初次加载、加载更多，以及重新筛选
    PeopleListQuery.get(params, function(response) {

      console.log('request', params);

      var data;

      // console.log(response);

      if (response.errno === 0) {

        var data = response.data;

        if (data.length > 0) {
          // 追加室友列表并更新页数
          if (params.p === 1) {
            $ionicScrollDelegate.scrollTop();
          }
          $scope.list = params.p === 1 ? data : $scope.list.concat(response.data);
          PeopleFilterModel.increasePage();
        } else {

          console.log('no more');
          $scope.hasMore = false;
          // PeopleFilterModel.setMore(false);
        }

        PeopleFilterModel.setUsingCache(true);
      } else {

        // TODO error handling
        $scope.hasMore = true;
        // PeopleFilterModel.setMore(true);
      }

      // 关闭 loading 动画
      $ionicLoading.hide();
      $scope.$broadcast('scroll.infiniteScrollComplete');

    }, function(err) {

      console.log('err', err);

      $ionicLoading.hide();
      // PeopleFilterModel.setMore(false);
      $scope.hasMore = false;
      $scope.$broadcast('scroll.infiniteScrollComplete');
    });
  };

  $scope.$on('$stateChangeSuccess', function(event, toState) {
    if (toState.name === 'menu.people-list' && !PeopleFilterModel.isUsingCache()) {
      $scope.loadMore();
      PeopleFilterModel.setUsingCache(true);
    }
  });

  // 在跳转到室友详情之前，先判断是否填完个人信息
  $scope.jumpToDetail = function(hash) {

    // console.log(PersonalInfo);
    // PersonalInfo.completeInfo = false;
    // PersonalInfo.completeAsk = true;

    // TODO 判断是否完成问卷
    if (PersonalInfo.completeInfo && PersonalInfo.completeAsk) {
      $scope.go(hash);
    } else if (!PersonalInfo.completeAsk && !PersonalInfo.completeInfo) {
      $ionicPopup.confirm({
        template: '只有填写自己的个人信息和匹配问题才能为您匹配室友，并查看详情信息哟。',
        okText: '现在填写',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me-register');
        } else {
          // do nothing
        }
      });
    } else if (!PersonalInfo.completeInfo) {
      $ionicPopup.confirm({
        template: '只有填写自己的个人信息才能看到室友的详情信息哟。',
        okText: '现在填写',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me-register');
        } else {
          // do nothing
        }
      });
    } else if (!PersonalInfo.completeAsk) {
      $ionicPopup.confirm({
        template: '请回答以下7个问题，以便为您更精确匹配室友。',
        okText: '现在回答',
        cancelText: '稍后再说'
      }).then(function(res) {
        if (res) {
          $scope.go('/me/q/1');
        } else {
          // do nothing
        }
      });
    }
  };
})

.controller('PeopleFilterCtrl', function($scope, $state, PeopleFilterModel) {
  $scope.buttons = PeopleFilterModel.radio;
  $scope.list = PeopleFilterModel.list;
  $scope.params = PeopleFilterModel.params();

  $scope.finish = function() {
    PeopleFilterModel.resetPage();
    PeopleFilterModel.setMore(true);
    PeopleFilterModel.setUsingCache(false);
    $state.go('menu.people-list');
  };

  $scope.back = function() {
    PeopleFilterModel.setUsingCache(true);
    $state.go('menu.people-list');
  };
})

.controller('PeopleDetailWrapperCtrl',
  function($scope, $ionicActionSheet, $stateParams, PersonalInfo, $ionicPopup,
    PeopleDetailQuery, FavAdd, FavRemove, ForbidAdd) {

  PeopleDetailQuery.get({id: $stateParams.id}, function(response) {
    console.log('detail', response);

    if (response.errno === 0) {
      $scope.people = response.data;
      $scope.house = $scope.people.matchUserHouse;
      // console.log($scope.house);
    }
  }, function(err) {
    console.log('error', err);
  });

  // 显示 收藏/屏幕 菜单
  $scope.showMenu = function() {

    // 返回一个关闭菜单的函数
    $ionicActionSheet.show({
      buttons: [
        { text: $scope.people.isFav ? '取消收藏' : '收藏' },
        { text: '不喜欢' }
      ],
      cancelText: '取消',
      buttonClicked: function(index) {

        if (index === 0) {

          // 处理添加收藏或者取消收藏
          console.log('fav');

          if ($scope.people.isFav) {

            // 取消收藏
            FavRemove.save({userId: PersonalInfo.userId, favId: $scope.people.userId},
              function(response) {
                if (response.errno === 0) {
                  $scope.people.isFav = false;
                }
              }, function(err) {
                console.log('err', err);
              });

          } else {

            // 添加收藏
            FavAdd.save({userId: PersonalInfo.userId, favId: $scope.people.userId},
              function(response) {
                if (response.errno === 0) {
                  $scope.people.isFav = true;
                }
              }, function(err) {
                console.log('err', err);
              });
          }
        } else if (index === 1) {

          // 处理屏蔽
          console.log('forbid');

          ForbidAdd.save({userId: PersonalInfo.userId, forbidId: $scope.people.userId},
            function(response) {
              if (response.errno === 0) {
                console.log('forbid success');
              }
            }, function(err) {
              console.log('err', err);
            });
        }

        return true;
      }
    });
  };

  $scope.sendMessage = function() {

    if (SMS) {
      $ionicPopup.prompt({
        title: '短信',
        template: '请输入短信内容',
        inputPlaceholder: '短信内容'
      }).then(function(res) {
        console.log(res);

        if (res || res.trim().length == 0) return;

        SMS.sendSMS($scope.people.tel, res, function() {
          // console.log('success');
          $ionicPopup.alert({
            template: '发送成功'
          });
        }, function(){
          // console('failure');
          $ionicPopup.alert({
            template: '发送失败'
          });
        });
      });
     }
  };
})

.controller('PeopleDetailInfoCtrl', function($scope) {
  $scope.showInfo = true;
  // console.log($scope.people);
})

.controller('PeopleDetailHouseCtrl', function($scope) {
  $scope.showHouse = true;
  // console.log($scope.house);
})

.controller('FavCtrl', function($scope, PersonalInfo, FavQuery) {

  FavQuery.get({userId: PersonalInfo.userId}, function(response) {
    console.log('fav list', response);

    if (response.errno === 0) {
      $scope.list = response.data;
    }
  }, function(err) {
    console.log('err', err);
  });

  // 可以直接查看详情，不需要其他权限
  $scope.jumpToDetail = $scope.go;
})
;<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict'

const async = require('async')
require('colors')

const path = require('path')
const childProcess = require('child_process')
const CONFIG = require('./config')

const packagedAppPath = path.resolve(__dirname, '..', 'out', 'Atom-darwin-x64')
const executablePath = path.join(packagedAppPath, 'Atom.app', 'Contents', 'MacOS', 'Atom')
const resourcePath = CONFIG.repositoryRootPath

function runCoreMainProcessTests (callback) {
  const testPath = path.join(CONFIG.repositoryRootPath, 'spec', 'main-process')
  const testArguments = [
    '--resource-path', resourcePath,
    '--test', '--main-process', testPath
  ]

  console.log('Executing core main process tests...'.bold.green)
  const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
  cp.on('error', error => { callback(error) })
  cp.on('close', exitCode => { callback(null, exitCode) })
}

function runCoreRenderProcessTests (callback) {
  const testPath = path.join(CONFIG.repositoryRootPath, 'spec')
  const testArguments = [
    '--resource-path', resourcePath,
    '--test', testPath
  ]

  console.log('Executing core render process tests...'.bold.green)
  const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
  cp.on('error', error => { callback(error) })
  cp.on('close', exitCode => { callback(null, exitCode) })
}

const testSuitesToRun = [runCoreMainProcessTests, runCoreRenderProcessTests]

async.parallelLimit(testSuitesToRun, 2, function (err, exitCodes) {
  if (err) {
    console.error(err)
    process.exit(1)
  } else {
    const testsPassed = exitCodes.every(exitCode => exitCode === 0)
    process.exit(testsPassed ? 0 : 1)
  }
})
<commit_msg>Run package tests in test.js<commit_after>#!/usr/bin/env node

'use strict'

require('colors')
const async = require('async')
const childProcess = require('child_process')
const fs = require('fs')
const path = require('path')

const CONFIG = require('./config')

const packagedAppPath = path.resolve(__dirname, '..', 'out', 'Atom-darwin-x64')
const executablePath = path.join(packagedAppPath, 'Atom.app', 'Contents', 'MacOS', 'Atom')
const resourcePath = CONFIG.repositoryRootPath

function runCoreMainProcessTests (callback) {
  const testPath = path.join(CONFIG.repositoryRootPath, 'spec', 'main-process')
  const testArguments = [
    '--resource-path', resourcePath,
    '--test', '--main-process', testPath
  ]

  console.log('Executing core main process tests...'.bold.green)
  const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
  cp.on('error', error => { callback(error) })
  cp.on('close', exitCode => { callback(null, exitCode) })
}

function runCoreRenderProcessTests (callback) {
  const testPath = path.join(CONFIG.repositoryRootPath, 'spec')
  const testArguments = [
    '--resource-path', resourcePath,
    '--test', testPath
  ]

  console.log('Executing core render process tests...'.bold.green)
  const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
  cp.on('error', error => { callback(error) })
  cp.on('close', exitCode => { callback(null, exitCode) })
}

const packageTestSuites = []
for (let packageName in CONFIG.appMetadata.packageDependencies) {
  const packageSpecDirPath = path.join(CONFIG.repositoryRootPath, 'node_modules', packageName, 'spec')
  if (!fs.existsSync(packageSpecDirPath)) continue

  packageTestSuites.push(function (callback) {
    const testArguments = [
      '--resource-path', resourcePath,
      '--test', packageSpecDirPath
    ]

    console.log(`Executing ${packageName} tests...`.bold.green)
    const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
    cp.on('error', error => { callback(error) })
    cp.on('close', exitCode => { callback(null, exitCode) })
  })
}

const testSuitesToRun = [runCoreMainProcessTests, runCoreRenderProcessTests].concat(packageTestSuites)

async.parallelLimit(testSuitesToRun, 2, function (err, exitCodes) {
  if (err) {
    console.error(err)
    process.exit(1)
  } else {
    const testsPassed = exitCodes.every(exitCode => exitCode === 0)
    process.exit(testsPassed ? 0 : 1)
  }
})
<|endoftext|>


<commit_before>function ShareTest() {
  FB.ui(
   {
    method: 'share_open_graph',
	action_type: 'og.likes',
	action_properties: JSON.stringify({
    object:'https://eziofilho.github.io/new_layout/0.html',
	})
   }, function(response){

    console.log(response);

   });
  }

  window.fbAsyncInit = function() {
    FB.init({
      appId      : '334298370360424',
      xfbml      : true,
      version    : 'v2.10'
    });
    FB.AppEvents.logPageView();
  };

(function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/pt_BR/sdk.js#xfbml=1&version=v2.10";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
<commit_msg>fbshare2<commit_after>function ShareTest() {
  FB.ui(
   {
    method: 'share_open_graph',
	action_type: 'og.likes',
	action_properties: JSON.stringify({
		object:'https://eziofilho.github.io/new_layout/0.html',
	})
   }, function(response){

    console.log(response);

   });
  }

  window.fbAsyncInit = function() {
    FB.init({
      appId      : '334298370360424',
      xfbml      : true,
      version    : 'v2.10'
    });
    FB.AppEvents.logPageView();
  };

(function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/pt_BR/sdk.js#xfbml=1&version=v2.10";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
<|endoftext|>


<commit_before>var fs = require('fs');
var moduleInstance = require('./moduleInstance');
var M = process.mono;

// get cached miid and check role access
function getCachedMiid (miid, roleId) {

    miid = M.cache.miids.get(miid);

    // send client config from cache
    if (
        miid &&
        (
            // check access
            miid.mono.roles['*'] ||
            miid.mono.roles[roleId]
        ) &&
        miid.mono.client
    ) {
        return miid;
    }

    return null;
}

// load miid configuration (ws)
function load (err, miid) {
    var self = this;
    var session = self.link.ws.session;

    // send client config from cache
    var cachedMiid = getCachedMiid(miid, session[M.config.session.role]);
    if (cachedMiid) {
        return self.emit('config', null, cachedMiid.mono.client);
    }

    // load and init module
    moduleInstance.load(miid, session[M.config.session.role], function (err, config) {

        if (err) {
            return self.emit('config', err || 'Module not found');
        }

        // handle i18n html
        if (typeof config.html === 'object') {
            config.html = config.html[session[M.config.session.locale]] ? config.html[session[M.config.session.locale]] : 'no html found';
        }

        // return client config
        self.emit('config', null, config);
    });
}

// get html snipptets (ws)
function html (err, data) {
    var self = this;

    if (!data) {
        return self.emit('html', 'No path given');
    }

    var file = M.config.paths.PUBLIC_ROOT + data.replace(/[^a-z0-9\/\.\-_]|\.\.\//gi, "");
    fs.readFile(file, {encoding: 'utf8'}, function (err, data) {

        if (err) {
            return self.emit('html', 'File not found');
        }

        self.emit('html', null, data);
    });
}

// browser modules (http)
function moduleFiles () {
    var self = this;

    var miid = self.link.path[0];
    var path = self.link.path[1];

    // check if request format is correct
    if (!miid || !path) {
        return self.link.send(400, "Incorrect module request URL format");
    }

    // the module name must be almost alphanumeric
    if (self.link.pathname.replace(/[^a-z0-9\/\.\-_@]|\.\.\//gi, "") !== self.link.pathname) {
        return self.link.send(400, "Incorrect data in module request URL");
    }

    // get miid from cache

    var cachedMiid = getCachedMiid(miid, self.link.req.session[M.config.session.role]);
    if (cachedMiid) {

        // handle compression
        if (M.config.compressFiles) {
            self.link.res.setHeader('content-encoding', 'gzip');
            self.link.res.setHeader('vary', 'accept-encoding');
        }

        // overwrite url
        self.link.req.url = cachedMiid.mono.name + path;

        // server file
        return M.file.module.serve(self.link.req, self.link.res);
    }

    self.link.send(404, 'Miid not found');
}

function dependency () {
    var self = this;

    var source = self.link.path[0];
    var owner = self.link.path[1];
    var name = self.link.path[2];
    var version = self.link.path[3];

    // check if request format is correct
    if (!source || !owner || !name || !version) {
        return self.link.send(400, "Incorrect module request URL format");
    }

    // the module name must be almost alphanumeric
    if (self.link.pathname.replace(/[^a-z0-9\/\.\-_@]|\.\.\//gi, "") !== self.link.pathname) {
        return self.link.send(400, "Incorrect data in module request URL");
    }

    // handle compression
    if (M.config.compressFiles) {
        self.link.res.setHeader('content-encoding', 'gzip');
        self.link.res.setHeader('vary', 'accept-encoding');
    }

    // overwrite url source + '/' + owner + '/' + name + '/' + version + '/' +
    self.link.req.url = self.link.path.join('/');

    // server file
    return M.file.module.serve(self.link.req, self.link.res);
}

// get mono client (http)
function client (){
    var self = this;

    if (M.config.compressFiles) {
        self.link.res.setHeader('content-encoding', 'gzip');
        self.link.res.setHeader('vary', 'accept-encoding');
        self.link.req.url = link.path[0].split('.')[0] + '.min.gz';

    } else {
        self.link.req.url = self.link.path[0];
    }

    M.file.client.serve(self.link.req, self.link.res);
}

function init (config) {
    var self = this;

    // setup client event interface
    self.on('load', load);
    self.on('mod', moduleFiles);
    self.on('dep', dependency);
    self.on('client', client);
    self.on('getHtml', html);
}

module.exports = init;
<commit_msg>removed getHtml function. this is now handled by layout<commit_after>var moduleInstance = require('./moduleInstance');
var M = process.mono;

// get cached miid and check role access
function getCachedMiid (miid, roleId) {

    miid = M.cache.miids.get(miid);

    // send client config from cache
    if (
        miid &&
        (
            // check access
            miid.mono.roles['*'] ||
            miid.mono.roles[roleId]
        ) &&
        miid.mono.client
    ) {
        return miid;
    }

    return null;
}

// load miid configuration (ws)
function load (err, miid) {
    var self = this;
    var session = self.link.ws.session;

    // send client config from cache
    var cachedMiid = getCachedMiid(miid, session[M.config.session.role]);
    if (cachedMiid) {
        return self.emit('config', null, cachedMiid.mono.client);
    }

    // load and init module
    moduleInstance.load(miid, session[M.config.session.role], function (err, config) {

        if (err) {
            return self.emit('config', err || 'Module not found');
        }

        // handle i18n html
        if (typeof config.html === 'object') {
            config.html = config.html[session[M.config.session.locale]] ? config.html[session[M.config.session.locale]] : 'no html found';
        }

        // return client config
        self.emit('config', null, config);
    });
}

// browser modules (http)
function moduleFiles () {
    var self = this;

    var miid = self.link.path[0];
    var path = self.link.path[1];

    // check if request format is correct
    if (!miid || !path) {
        return self.link.send(400, "Incorrect module request URL format");
    }

    // the module name must be almost alphanumeric
    if (self.link.pathname.replace(/[^a-z0-9\/\.\-_@]|\.\.\//gi, "") !== self.link.pathname) {
        return self.link.send(400, "Incorrect data in module request URL");
    }

    // get miid from cache

    var cachedMiid = getCachedMiid(miid, self.link.req.session[M.config.session.role]);
    if (cachedMiid) {

        // handle compression
        if (M.config.compressFiles) {
            self.link.res.setHeader('content-encoding', 'gzip');
            self.link.res.setHeader('vary', 'accept-encoding');
        }

        // overwrite url
        self.link.req.url = cachedMiid.mono.name + path;

        // server file
        return M.file.module.serve(self.link.req, self.link.res);
    }

    self.link.send(404, 'Miid not found');
}

// load module dependency (http)
function dependency () {
    var self = this;

    var source = self.link.path[0];
    var owner = self.link.path[1];
    var name = self.link.path[2];
    var version = self.link.path[3];

    // check if request format is correct
    if (!source || !owner || !name || !version) {
        return self.link.send(400, "Incorrect module request URL format");
    }

    // the module name must be almost alphanumeric
    if (self.link.pathname.replace(/[^a-z0-9\/\.\-_@]|\.\.\//gi, "") !== self.link.pathname) {
        return self.link.send(400, "Incorrect data in module request URL");
    }

    // handle compression
    if (M.config.compressFiles) {
        self.link.res.setHeader('content-encoding', 'gzip');
        self.link.res.setHeader('vary', 'accept-encoding');
    }

    // overwrite url source + '/' + owner + '/' + name + '/' + version + '/' +
    self.link.req.url = self.link.path.join('/');

    // server file
    return M.file.module.serve(self.link.req, self.link.res);
}

// get mono client (http)
function client (){
    var self = this;

    if (M.config.compressFiles) {
        self.link.res.setHeader('content-encoding', 'gzip');
        self.link.res.setHeader('vary', 'accept-encoding');
        self.link.req.url = link.path[0].split('.')[0] + '.min.gz';

    } else {
        self.link.req.url = self.link.path[0];
    }

    M.file.client.serve(self.link.req, self.link.res);
}

function init (config) {
    var self = this;

    // setup client event interface
    self.on('load', load);
    self.on('mod', moduleFiles);
    self.on('dep', dependency);
    self.on('client', client);
}

module.exports = init;
<|endoftext|>


<commit_before>/*
 * app/lib/commands/layer/drawLine.js
 *
 *
 * Copyright (C) 2015  Pierre Marchand <pierremarc07@gmail.com>
 *
 * License in LICENSE file at the root of the repository.
 *
 */

// 'use strict';

var _ = require('underscore'),
    Promise = require('bluebird'),
    Geometry = require('../Geometry'),
    semaphore = require('../Semaphore'),
    paper = require('../../vendors/paper');

function setupCanvas (container, view) {
    var canvas = document.createElement('canvas'),
        rect = view.getRect();
    canvas.setAttribute('class', 'tool-draw');
    canvas.style.position = 'absolute';
    canvas.width = rect.width;
    canvas.height = rect.height;
    canvas.style.top = rect.top + 'px';
    canvas.style.left = rect.left + 'px';
    canvas.backgroundColor = 'transparent';
    var hints = document.createElement('div');
    hints.setAttribute('class', 'draw-hints');
    hints.innerHTML = "click and hold on map to draw";
    container.appendChild(hints);
    container.appendChild(canvas);
    paper.setup(canvas);
    paper.view.draw();
    semaphore.on('view:resize', function () {
        var vrect = view.getRect();
        canvas.width = vrect.width;
        canvas.height = vrect.height;
        canvas.style.top = vrect.top + 'px';
        canvas.style.left = vrect.left + 'px';
    }, this);
}

function drawLine () {
    var self = this,
        shell = self.shell,
        stdout = shell.stdout,
        terminal = shell.terminal,
        map = shell.env.map,
        display = terminal.display();

    setupCanvas(display.node, map.getView());

    var resolver = function (resolve, reject) {

        var path,
            points =[],
            tool = new paper.Tool();

        var onMouseDown = function (event) {
            path = new paper.Path({
                segments: [event.point],
                strokeColor: 'black',
                fullySelected: true
            });
        };

        var onMouseDrag = function (event) {
            path.add(event.point);
        };

        var onMouseUp = function (event) {
            var segmentCount = path.segments.length;
            console.log(path);
            var polyLineOrGon; // TODO populate
            if (path.closed) {
                console.log('errr not implemted');
            }
            else {
                var line = new Geometry.LineString([]);
                var segments = path.segments;
                for (var i = 0; i < segments.length; i++) {
                    var s = segments[i],
                        pixel = [s.point.x, s.point.y];
                    line.appendCoordinate(map.getCoordinateFromPixel(pixel));
                }
            }

            tool.off('mousedown', onMouseDown);
            tool.off('mousedrag', onMouseDrag);
            tool.off('mouseup', onMouseUp);
            tool.remove();
            paper.project.remove();
            display.end();
            resolve(line);
        };

        tool.on('mousedown', onMouseDown);
        tool.on('mousedrag', onMouseDrag);
        tool.on('mouseup', onMouseUp);
    };

    return (new Promise(resolver));
}

module.exports = exports = {
    name: 'draw',
    command: drawLine
};
<commit_msg>added cancel button on draw<commit_after>/*
 * app/lib/commands/layer/drawLine.js
 *
 *
 * Copyright (C) 2015  Pierre Marchand <pierremarc07@gmail.com>
 *
 * License in LICENSE file at the root of the repository.
 *
 */

// 'use strict';

var _ = require('underscore'),
    Promise = require('bluebird'),
    Geometry = require('../Geometry'),
    semaphore = require('../Semaphore'),
    paper = require('../../vendors/paper'),
    helpers = require('../helpers');

var makeButton = helpers.makeButton,
    addClass = helpers.addClass;

function setupCanvas (container, view) {
    var canvas = document.createElement('canvas'),
        rect = view.getRect();

    // setup canvas properties
    canvas.setAttribute('class', 'tool-draw');
    canvas.style.position = 'absolute';
    canvas.width = rect.width;
    canvas.height = rect.height;
    canvas.style.top = rect.top + 'px';
    canvas.style.left = rect.left + 'px';
    canvas.backgroundColor = 'transparent';

    //
    container.appendChild(canvas);
    paper.setup(canvas);
    paper.view.draw();

    semaphore.on('view:resize', function () {
        var vrect = view.getRect();
        canvas.width = vrect.width;
        canvas.height = vrect.height;
        canvas.style.top = vrect.top + 'px';
        canvas.style.left = vrect.left + 'px';
    }, this);
}

function insertLeftPannel (container, closer) {
    var wrapper = document.createElement('div'),
        infos = document.createElement('div'),
        button = makeButton('Cancel', {}, closer);

    wrapper.appendChild(infos);
    wrapper.appendChild(button);
    container.appendChild(wrapper);
}

function drawLine () {
    var self = this,
        shell = self.shell,
        stdout = shell.stdout,
        terminal = shell.terminal,
        map = shell.env.map,
        display = terminal.display();

    setupCanvas(display.node, map.getView());

    var resolver = function (resolve, reject) {
        var path,
            points =[],
            tool = new paper.Tool();

        var closer = function (arg) {
            display.end();
            if (arg) {
                return resolve(arg);
            }
            reject('Cancelled');
        };

        var onMouseDown = function (event) {
            path = new paper.Path({
                segments: [event.point],
                strokeColor: 'black',
                fullySelected: true
            });
        };

        var onMouseDrag = function (event) {
            path.add(event.point);
        };

        var onMouseUp = function (event) {
            var segmentCount = path.segments.length;
            console.log(path);
            var polyLineOrGon; // TODO populate
            if (path.closed) {
                console.log('errr not implemted');
            }
            else {
                var line = new Geometry.LineString([]);
                var segments = path.segments;
                for (var i = 0; i < segments.length; i++) {
                    var s = segments[i],
                        pixel = [s.point.x, s.point.y];
                    line.appendCoordinate(map.getCoordinateFromPixel(pixel));
                }
            }

            tool.off('mousedown', onMouseDown);
            tool.off('mousedrag', onMouseDrag);
            tool.off('mouseup', onMouseUp);
            tool.remove();

            paper.project.remove();
            closer(line);
        };

        insertLeftPannel(display.node, closer);
        tool.on('mousedown', onMouseDown);
        tool.on('mousedrag', onMouseDrag);
        tool.on('mouseup', onMouseUp);
    };

    return (new Promise(resolver));
}

module.exports = exports = {
    name: 'draw',
    command: drawLine
};
<|endoftext|>


<commit_before>'use strict';

// Load requirements
const path = require('path'),
      fs = require('fs'),
      os = require('os');

// Load modules
const utils = require(__base + 'libs/utils');

module.exports = {
  type: 'input',
  name: 'temp',
  message: 'Fantastic! Just one last thing before we move on, where is your temp directory?\n',
  default: os.tmpdir() + ( os.platform() === 'win32' ? '\\' : '/' ),
  when: () => {
    return true;
  },
  validate: (val, answers) => {

    if ( ! val.match(/[/|\/]$/) ) {
      return 'Paths must end with a slash';
    }

    if ( ! fs.existsSync(val) || ! fs.lstatSync(val).isDirectory() ) {
      return 'Path either does not exist or is not a directory';
    }

    return true;
  }
};
<commit_msg>Update setup temp to use new language strings and add validation<commit_after>'use strict';

// Load requirements
const path = require('path'),
      fs = require('fs'),
      os = require('os');

// Load our modules
const utils = require(__base + 'libs/utils'),
      config = require('../config')();

module.exports = {
  type: 'input',
  name: 'temp',
  message: utils.colorString(lang('setup.temp.question')),
  default: os.tmpdir() + ( os.platform() === 'win32' ? '\\' : '/' ),
  when: () => {
    utils.output(lang('setup.temp.title'), lang('setup.temp.message.why') + '\n\n' + lang('setup.temp.message.options'), 4, 6);
    return true;
  },
  validate: (val, answers) => {

    val = val.trim();

    if( val === '') {
      return true;
    }

    if ( ! val.match(/[/|\/]$/) ) {
      return lang('setup.temp.validation.slash');
    }

    if ( ! fs.existsSync(val) || ! fs.lstatSync(val).isDirectory() ) {
      return lang('setup.temp.validation.not_found');
    }

    return true;
  }
};
<|endoftext|>


<commit_before>/**
 * @file CursorPopoverProvider component
 */

import React, {Component, cloneElement, createRef} from 'react';
import PropTypes from 'prop-types';

// components
import CursorPopover from '../CursorPopover';

// statics
import Theme from '../Theme';
import Position from '../_statics/Position';

// vendors
import {findDOMNode} from 'react-dom';
import Util from '../_vendors/Util';
import ComponentUtil from '../_vendors/ComponentUtil';

class CursorPopoverProvider extends Component {

    static Position = Position;
    static Theme = Theme;

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            visible: ComponentUtil.getDerivedState(props, state, 'visible')
        };
    }

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.trigger = createRef();
        this.triggerEl = null;

        this.state = {
            visible: props.visible
        };

    }

    componentDidMount() {
        this.triggerEl = findDOMNode(this.trigger?.current);
    }

    /**
     * public
     */
    show = () => {

        if (this.state.visible) {
            return;
        }

        this.setState({
            visible: true
        }, () => this.props.onRequestOpen?.());

    };

    /**
     * public
     */
    hide = () => {

        if (!this.state.visible) {
            return;
        }

        this.setState({
            visible: false
        }, () => this.props.onRequestClose?.());

    };

    /**
     * public
     */
    toggle = () => {
        this.setState({
            visible: !this.state.visible
        }, () => {
            if (!this.state.visible) {
                this.props.onRequestClose?.();
            } else {
                this.props.onRequestOpen?.();
            }
        });
    };

    handleMouseOver = e => {
        this.props?.children?.props?.onMouseOver?.(e);
        this.show();
    };

    render() {

        const {

            children, popoverContent,

            // not passing down these props
            // eslint-disable-next-line no-unused-vars
            onRequestOpen, onRequestClose,

            ...restProps

        } = this.props;
        const {visible} = this.state;

        if (!popoverContent) {
            return children;
        }

        return (
            <>

                {
                    cloneElement(children, {
                        ref: this.trigger,
                        onMouseOver: this.handleMouseOver
                    })
                }

                <CursorPopover {...restProps}
                               triggerEl={this.triggerEl}
                               visible={visible}
                               onRequestClose={this.hide}>
                    {popoverContent}
                </CursorPopover>

            </>
        );
    }

}

CursorPopoverProvider.propTypes = {

    children: PropTypes.any,

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the content element.
     */
    contentClassName: PropTypes.string,

    modalClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    parentEl: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be CursorPopover.Position.LEFT or CursorPopover.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * If true, popover will have animation effects.
     */
    isAnimated: PropTypes.bool,

    enterable: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    shouldFollowScroll: PropTypes.bool,
    scrollEl: PropTypes.object,

    resetPositionWait: PropTypes.number,

    popoverContent: PropTypes.any,

    /**
     * The function of popover render.
     */
    onRender: PropTypes.func,

    /**
     * The function of popover rendered.
     */
    onRendered: PropTypes.func,

    /**
     * The function of popover destroy.
     */
    onDestroy: PropTypes.func,

    /**
     * The function of popover destroyed.
     */
    onDestroyed: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be opened.
     */
    onRequestOpen: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    onCursorMove: PropTypes.func,
    onTargetChange: PropTypes.func

};

CursorPopoverProvider.defaultProps = {

    parentEl: document.body,

    visible: false,
    theme: Theme.DEFAULT,
    position: Position.BOTTOM_LEFT,
    isAnimated: true,
    enterable: false,

    shouldFollowScroll: false,
    resetPositionWait: 250

};

export default CursorPopoverProvider;
<commit_msg>update CursorPopoverProvider<commit_after>/**
 * @file CursorPopoverProvider component
 */

import React, {Component, cloneElement, createRef} from 'react';
import PropTypes from 'prop-types';

// components
import CursorPopover from '../CursorPopover';

// statics
import Theme from '../Theme';
import Position from '../_statics/Position';

// vendors
import {findDOMNode} from 'react-dom';
import Util from '../_vendors/Util';
import ComponentUtil from '../_vendors/ComponentUtil';

class CursorPopoverProvider extends Component {

    static Position = Position;
    static Theme = Theme;

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            visible: ComponentUtil.getDerivedState(props, state, 'visible')
        };
    }

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.trigger = createRef();
        this.triggerEl = null;

        this.state = {
            visible: props.visible
        };

    }

    componentDidMount() {
        this.triggerEl = findDOMNode(this.trigger?.current);
    }

    /**
     * public
     */
    show = () => {

        if (this.state.visible) {
            return;
        }

        this.setState({
            visible: true
        }, () => this.props.onRequestOpen?.());

    };

    /**
     * public
     */
    hide = () => {

        if (!this.state.visible) {
            return;
        }

        this.setState({
            visible: false
        }, () => this.props.onRequestClose?.());

    };

    /**
     * public
     */
    toggle = () => {
        this.setState({
            visible: !this.state.visible
        }, () => {
            if (!this.state.visible) {
                this.props.onRequestClose?.();
            } else {
                this.props.onRequestOpen?.();
            }
        });
    };

    handleMouseEnter = e => {
        this.props?.children?.props?.onMouseEnter?.(e);
        this.show();
    };

    render() {

        const {

            children, popoverContent,

            // not passing down these props
            // eslint-disable-next-line no-unused-vars
            onRequestOpen, onRequestClose,

            ...restProps

        } = this.props;
        const {visible} = this.state;

        if (!popoverContent) {
            return children;
        }

        return (
            <>

                {
                    cloneElement(children, {
                        ref: this.trigger,
                        onMouseEnter: this.handleMouseEnter
                    })
                }

                <CursorPopover {...restProps}
                               triggerEl={this.triggerEl}
                               visible={visible}
                               onRequestClose={this.hide}>
                    {popoverContent}
                </CursorPopover>

            </>
        );
    }

}

CursorPopoverProvider.propTypes = {

    children: PropTypes.any,

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the content element.
     */
    contentClassName: PropTypes.string,

    modalClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    parentEl: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be CursorPopover.Position.LEFT or CursorPopover.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * If true, popover will have animation effects.
     */
    isAnimated: PropTypes.bool,

    enterable: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    shouldFollowScroll: PropTypes.bool,
    scrollEl: PropTypes.object,

    resetPositionWait: PropTypes.number,

    popoverContent: PropTypes.any,

    /**
     * The function of popover render.
     */
    onRender: PropTypes.func,

    /**
     * The function of popover rendered.
     */
    onRendered: PropTypes.func,

    /**
     * The function of popover destroy.
     */
    onDestroy: PropTypes.func,

    /**
     * The function of popover destroyed.
     */
    onDestroyed: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be opened.
     */
    onRequestOpen: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    onCursorMove: PropTypes.func,
    onTargetChange: PropTypes.func

};

CursorPopoverProvider.defaultProps = {

    parentEl: document.body,

    visible: false,
    theme: Theme.DEFAULT,
    position: Position.BOTTOM_LEFT,
    isAnimated: true,
    enterable: false,

    shouldFollowScroll: false,
    resetPositionWait: 250

};

export default CursorPopoverProvider;
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'
import Playback from 'base/playback'
import Browser from 'components/browser'
import Log from 'plugins/log'

const AUTO = -1

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }

  get levels() { return this._levels || [] }
  get currentLevel() {
    if (this._currentLevel === null || this._currentLevel === undefined) {
      return AUTO;
    } else {
      return this._currentLevel; //0 is a valid level ID
    }
  }
  set currentLevel(id) {
    this._currentLevel = id
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START)
    this.hls.currentLevel = this._currentLevel
  }

  constructor(options) {
    super(options)
    this.minDvrSize = (options.hlsMinimumDvrSize === undefined) ? 60 : options.hlsMinimumDvrSize
    this.playbackType = Playback.VOD
    // for hls streams which have dvr with a sliding window,
    // the content at the start of the playlist is removed as new
    // content is appended at the end.
    // this means the actual playable start time will increase as the
    // start content is deleted
    // For streams with dvr where the entire recording is kept from the
    // beginning this should stay as 0
    this.playableRegionStartTime = 0
    // if content is removed from the beginning then this empty area should
    // be ignored. "playableRegionDuration" does not consider this
    this.playableRegionDuration = 0
    options.autoPlay && this.setupHls()
    this.recoverAttemptsRemaining = options.hlsRecoverAttempts || 16
  }

  setupHls() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MEDIA_ATTACHED, () => this.hls.loadSource(this.options.src))
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.on(HLSJS.Events.LEVEL_UPDATED, (evt, data) => this.onLevelUpdated(evt, data))
    this.hls.on(HLSJS.Events.LEVEL_SWITCH, (evt,data) => this.onLevelSwitch(evt, data))
    this.hls.on(HLSJS.Events.FRAG_LOADED, (evt, data) => this.onFragmentLoaded(evt, data))
    this.hls.on(HLSJS.Events.ERROR, (evt, data) => this.onError(evt, data))
    this.hls.attachMedia(this.el)
  }

  recover() {
    if (!this._recoveredDecodingError) {
      this._recoveredDecodingError = true
      this.hls.recoverMediaError()
    } else if (!this._recoveredAudioCodecError) {
      this._recoveredAudioCodecError = true
      this.hls.swapAudioCodec()
      this.hls.recoverMediaError()
    } else {
        // failed recovery
    }
  }

  // override
  setupSrc(srcUrl) {
    // this playback manages the src on the video element itself
  }

  // the duration on the video element itself should not be used
  // as this does not necesarily represent the duration of the stream
  // https://github.com/clappr/clappr/issues/668#issuecomment-157036678
  getDuration() {
    return this.playableRegionDuration
  }

  getCurrentTime() {
    return this.el.currentTime - this.playableRegionStartTime
  }

  // the time that "0" now represents relative to when playback started
  // for a stream with a sliding window this will increase as content is
  // removed from the beginning
  getStartTimeOffset() {
    return this.playableRegionStartTime
  }

  seekPercentage(percentage) {
    var seekTo = this.playableRegionDuration
    if (percentage > 0) {
      seekTo = this.playableRegionDuration * (percentage / 100)
    }
    this.seek(seekTo)
  }

  seek(time) {
    if (time < 0) {
      Log.warn("Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.")
      time = this.getDuration()
    }
    // assume live if time within 3 seconds of end of stream
    this.dvrEnabled && this.updateDvr(time < this.getDuration()-3)
    time += this.playableRegionStartTime
    super.seek(time)
  }

  seekToLivePoint() {
    this.seek(this.getDuration())
  }

  updateDvr(status) {
    this.trigger(Events.PLAYBACK_DVR, status)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': status})
  }

  updateSettings() {
    if (this.playbackType === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  onError(evt, data) {
    if (data && data.fatal && this.recoverAttemptsRemaining > 0) {
      this.recoverAttemptsRemaining -= 1
      switch (data.type) {
        case HLSJS.ErrorTypes.NETWORK_ERROR:
          Log.warning(`hlsjs: trying to recover from network error, evt ${evt}, data ${data} `)
          this.hls.startLoad()
          break
        case HLSJS.ErrorTypes.MEDIA_ERROR:
          Log.warning(`hlsjs: trying to recover from media error, evt ${evt}, data ${data} `)
          this.recover()
          break
        default:
          Log.error(`hlsjs: could not recover from error, evt ${evt}, data ${data} `)
          break
      }
    }
  }

  onTimeUpdate() {
    this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: this.getCurrentTime(), total: this.getDuration()}, this.name)
  }

  play() {
    if (!this.hls) {
      this.setupHls()
    }
    super.play()
  }

  pause() {
    super.pause()
    if (this.dvrEnabled) {
      this.updateDvr(true)
    }
  }

  stop() {
    super.stop()
    if (this.hls) {
      this.hls.destroy()
      delete this.hls
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? Playback.LIVE : Playback.VOD
    this.fillLevels()
    this.onLevelUpdated(evt, data)
  }

  fillLevels() {
    this._levels = this.hls.levels.map((level, index) => {
      return {id: index, level: level, label: `${level.bitrate/1000}Kbps`
    }})
    this.trigger(Events.PLAYBACK_LEVELS_AVAILABLE, this._levels)
  }

  onLevelUpdated(evt, data) {
    var fragments = data.details.fragments
    if (fragments.length > 0) {
      this.playableRegionStartTime = fragments[0].start
    }
    var newDuration = data.details.totalduration

    // if it's a live stream then shorten the duration to remove access
    // to the area after hlsjs's live sync point
    // seeks to areas after this point sometimes have issues
    if (this.playbackType === Playback.LIVE) {
      let currentLevel = this.hls.levels[data.level]
      let fragmentTargetDuration = currentLevel.details.targetduration
      let hlsjsConfig = this.options.hlsjsConfig || {}
      let liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || HLSJS.DefaultConfig.liveSyncDurationCount
      let hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount
      if (hiddenAreaDuration <= newDuration) {
        newDuration -= hiddenAreaDuration
      }
    }
    if (newDuration !== this.playableRegionDuration) {
      this.playableRegionDuration = newDuration
      this.onDurationChange()
    }
  }

  onFragmentLoaded(evt, data) {
    this.trigger(Events.PLAYBACK_FRAGMENT_LOADED, data)
  }

  onLevelSwitch(evt, data) {
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH_END)
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH, data)
    var currentLevel = this.hls.levels[data.level]
    if (currentLevel) {
      this.highDefinition = (currentLevel.height >= 720 || (currentLevel.bitrate / 1000) >= 2000);
      this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition)
      this.trigger(Events.PLAYBACK_BITRATE, {
        height: currentLevel.height,
        width: currentLevel.width,
        bandwidth: currentLevel.bandwidth,
        bitrate: currentLevel.bitrate,
        level: data.level
      })
    }
  }

  get dvrEnabled() {
    return (this.playableRegionDuration >= this.minDvrSize && this.getPlaybackType() === Playback.LIVE)
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === Playback.VOD || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource, mimeType) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = ((resourceParts.length > 1 && resourceParts[1] === "m3u8") ||
        mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl')

  return !!(HLSJS.isSupported() && isHls && !Browser.isSafari)
}
<commit_msg>hls: Fix invalid call to logger.<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'
import Playback from 'base/playback'
import Browser from 'components/browser'
import Log from 'plugins/log'

const AUTO = -1

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }

  get levels() { return this._levels || [] }
  get currentLevel() {
    if (this._currentLevel === null || this._currentLevel === undefined) {
      return AUTO;
    } else {
      return this._currentLevel; //0 is a valid level ID
    }
  }
  set currentLevel(id) {
    this._currentLevel = id
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START)
    this.hls.currentLevel = this._currentLevel
  }

  constructor(options) {
    super(options)
    this.minDvrSize = (options.hlsMinimumDvrSize === undefined) ? 60 : options.hlsMinimumDvrSize
    this.playbackType = Playback.VOD
    // for hls streams which have dvr with a sliding window,
    // the content at the start of the playlist is removed as new
    // content is appended at the end.
    // this means the actual playable start time will increase as the
    // start content is deleted
    // For streams with dvr where the entire recording is kept from the
    // beginning this should stay as 0
    this.playableRegionStartTime = 0
    // if content is removed from the beginning then this empty area should
    // be ignored. "playableRegionDuration" does not consider this
    this.playableRegionDuration = 0
    options.autoPlay && this.setupHls()
    this.recoverAttemptsRemaining = options.hlsRecoverAttempts || 16
  }

  setupHls() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MEDIA_ATTACHED, () => this.hls.loadSource(this.options.src))
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.on(HLSJS.Events.LEVEL_UPDATED, (evt, data) => this.onLevelUpdated(evt, data))
    this.hls.on(HLSJS.Events.LEVEL_SWITCH, (evt,data) => this.onLevelSwitch(evt, data))
    this.hls.on(HLSJS.Events.FRAG_LOADED, (evt, data) => this.onFragmentLoaded(evt, data))
    this.hls.on(HLSJS.Events.ERROR, (evt, data) => this.onError(evt, data))
    this.hls.attachMedia(this.el)
  }

  recover() {
    if (!this._recoveredDecodingError) {
      this._recoveredDecodingError = true
      this.hls.recoverMediaError()
    } else if (!this._recoveredAudioCodecError) {
      this._recoveredAudioCodecError = true
      this.hls.swapAudioCodec()
      this.hls.recoverMediaError()
    } else {
        // failed recovery
    }
  }

  // override
  setupSrc(srcUrl) {
    // this playback manages the src on the video element itself
  }

  // the duration on the video element itself should not be used
  // as this does not necesarily represent the duration of the stream
  // https://github.com/clappr/clappr/issues/668#issuecomment-157036678
  getDuration() {
    return this.playableRegionDuration
  }

  getCurrentTime() {
    return this.el.currentTime - this.playableRegionStartTime
  }

  // the time that "0" now represents relative to when playback started
  // for a stream with a sliding window this will increase as content is
  // removed from the beginning
  getStartTimeOffset() {
    return this.playableRegionStartTime
  }

  seekPercentage(percentage) {
    var seekTo = this.playableRegionDuration
    if (percentage > 0) {
      seekTo = this.playableRegionDuration * (percentage / 100)
    }
    this.seek(seekTo)
  }

  seek(time) {
    if (time < 0) {
      Log.warn("Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.")
      time = this.getDuration()
    }
    // assume live if time within 3 seconds of end of stream
    this.dvrEnabled && this.updateDvr(time < this.getDuration()-3)
    time += this.playableRegionStartTime
    super.seek(time)
  }

  seekToLivePoint() {
    this.seek(this.getDuration())
  }

  updateDvr(status) {
    this.trigger(Events.PLAYBACK_DVR, status)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': status})
  }

  updateSettings() {
    if (this.playbackType === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  onError(evt, data) {
    if (data && data.fatal && this.recoverAttemptsRemaining > 0) {
      this.recoverAttemptsRemaining -= 1
      switch (data.type) {
        case HLSJS.ErrorTypes.NETWORK_ERROR:
          Log.warn(`hlsjs: trying to recover from network error, evt ${evt}, data ${data} `)
          this.hls.startLoad()
          break
        case HLSJS.ErrorTypes.MEDIA_ERROR:
          Log.warn(`hlsjs: trying to recover from media error, evt ${evt}, data ${data} `)
          this.recover()
          break
        default:
          Log.error(`hlsjs: could not recover from error, evt ${evt}, data ${data} `)
          break
      }
    }
  }

  onTimeUpdate() {
    this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: this.getCurrentTime(), total: this.getDuration()}, this.name)
  }

  play() {
    if (!this.hls) {
      this.setupHls()
    }
    super.play()
  }

  pause() {
    super.pause()
    if (this.dvrEnabled) {
      this.updateDvr(true)
    }
  }

  stop() {
    super.stop()
    if (this.hls) {
      this.hls.destroy()
      delete this.hls
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? Playback.LIVE : Playback.VOD
    this.fillLevels()
    this.onLevelUpdated(evt, data)
  }

  fillLevels() {
    this._levels = this.hls.levels.map((level, index) => {
      return {id: index, level: level, label: `${level.bitrate/1000}Kbps`
    }})
    this.trigger(Events.PLAYBACK_LEVELS_AVAILABLE, this._levels)
  }

  onLevelUpdated(evt, data) {
    var fragments = data.details.fragments
    if (fragments.length > 0) {
      this.playableRegionStartTime = fragments[0].start
    }
    var newDuration = data.details.totalduration

    // if it's a live stream then shorten the duration to remove access
    // to the area after hlsjs's live sync point
    // seeks to areas after this point sometimes have issues
    if (this.playbackType === Playback.LIVE) {
      let currentLevel = this.hls.levels[data.level]
      let fragmentTargetDuration = currentLevel.details.targetduration
      let hlsjsConfig = this.options.hlsjsConfig || {}
      let liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || HLSJS.DefaultConfig.liveSyncDurationCount
      let hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount
      if (hiddenAreaDuration <= newDuration) {
        newDuration -= hiddenAreaDuration
      }
    }
    if (newDuration !== this.playableRegionDuration) {
      this.playableRegionDuration = newDuration
      this.onDurationChange()
    }
  }

  onFragmentLoaded(evt, data) {
    this.trigger(Events.PLAYBACK_FRAGMENT_LOADED, data)
  }

  onLevelSwitch(evt, data) {
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH_END)
    this.trigger(Events.PLAYBACK_LEVEL_SWITCH, data)
    var currentLevel = this.hls.levels[data.level]
    if (currentLevel) {
      this.highDefinition = (currentLevel.height >= 720 || (currentLevel.bitrate / 1000) >= 2000);
      this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition)
      this.trigger(Events.PLAYBACK_BITRATE, {
        height: currentLevel.height,
        width: currentLevel.width,
        bandwidth: currentLevel.bandwidth,
        bitrate: currentLevel.bitrate,
        level: data.level
      })
    }
  }

  get dvrEnabled() {
    return (this.playableRegionDuration >= this.minDvrSize && this.getPlaybackType() === Playback.LIVE)
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === Playback.VOD || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource, mimeType) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = ((resourceParts.length > 1 && resourceParts[1] === "m3u8") ||
        mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl')

  return !!(HLSJS.isSupported() && isHls && !Browser.isSafari)
}
<|endoftext|>


<commit_before>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var Plotly = require('../plotly');
var Lib = require('../lib');

var helpers = require('../snapshot/helpers');
var toSVG = require('../snapshot/tosvg');
var svgToImg = require('../snapshot/svgtoimg');

var getGraphDiv = require('./helpers').getGraphDiv;

var attrs = {
    format: {
        valType: 'enumerated',
        values: ['png', 'jpeg', 'webp', 'svg'],
        dflt: 'png',
        description: 'Sets the format of exported image.'
    },
    width: {
        valType: 'number',
        min: 1,
        description: [
            'Sets the exported image width.',
            'Defaults to the value found in `layout.width`'
        ].join(' ')
    },
    height: {
        valType: 'number',
        min: 1,
        description: [
            'Sets the exported image height.',
            'Defaults to the value found in `layout.height`'
        ].join(' ')
    },
    setBackground: {
        valType: 'any',
        dflt: false,
        description: [
            'Sets the image background mode.',
            'By default, the image background is determined by `layout.paper_bgcolor`,',
            'the *transparent* mode.',
            'One might consider setting `setBackground` to *opaque*',
            'when exporting a *jpeg* image as JPEGs do not support opacity.'
        ].join(' ')
    },
    imageDataOnly: {
        valType: 'boolean',
        dflt: false,
        description: [
            'Determines whether or not the return value is prefixed by',
            'the image format\'s corresponding \'data:image;\' spec.'
        ].join(' ')
    }
};

var IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;

/** Plotly.toImage
 *
 * @param {object | string | HTML div} gd
 *   can either be a data/layout/config object
 *   or an existing graph

 *   or an id to an existing graph

 * @param {object} opts (see above)
 * @return {promise}
 */
function toImage(gd, opts) {
    opts = opts || {};

    var data;
    var layout;
    var config;

    if(Lib.isPlainObject(gd)) {
        data = gd.data || [];
        layout = gd.layout || {};
        config = gd.config || {};
    } else {
        gd = getGraphDiv(gd);
        data = Lib.extendDeep([], gd.data);
        layout = Lib.extendDeep({}, gd.layout);
        config = gd._context;
    }

    function isBadlySet(attr) {
        return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
    }

    if(!isBadlySet('width') || !isBadlySet('height')) {
        throw new Error('Height and width should be pixel values.');
    }

    if(!isBadlySet('format')) {
        throw new Error('Image format is not jpeg, png, svg or webp.');
    }

    var fullOpts = {};

    function coerce(attr, dflt) {
        return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
    }

    var format = coerce('format');
    var width = coerce('width');
    var height = coerce('height');
    var setBackground = coerce('setBackground');
    var imageDataOnly = coerce('imageDataOnly');

    // put the cloned div somewhere off screen before attaching to DOM
    var clonedGd = document.createElement('div');
    clonedGd.style.position = 'absolute';
    clonedGd.style.left = '-5000px';
    document.body.appendChild(clonedGd);

    // extend layout with image options
    var layoutImage = Lib.extendFlat({}, layout);
    if(width) layoutImage.width = width;
    if(height) layoutImage.height = height;

    // extend config for static plot
    var configImage = Lib.extendFlat({}, config, {
        staticPlot: true,
        plotGlPixelRatio: config.plotGlPixelRatio || 2,
        setBackground: setBackground
    });

    var redrawFunc = helpers.getRedrawFunc(clonedGd);

    function wait() {
        return new Promise(function(resolve) {
            setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
        });
    }

    function convert() {
        return new Promise(function(resolve, reject) {
            var svg = toSVG(clonedGd);

            if(format === 'svg' && imageDataOnly) {
                return resolve(svg);
            }

            var canvas = document.createElement('canvas');
            canvas.id = Lib.randstr();

            svgToImg({
                format: format,
                width: clonedGd._fullLayout.width,
                height: clonedGd._fullLayout.height,
                canvas: canvas,
                svg: svg,
                // ask svgToImg to return a Promise
                //  rather than EventEmitter
                //  leave EventEmitter for backward
                //  compatibility
                promise: true
            })
            .then(function(url) {
                Plotly.purge(clonedGd);
                document.body.removeChild(clonedGd);
                resolve(url);
            })
            .catch(function(err) {
                reject(err);
            });
        });
    }

    function urlToImageData(url) {
        if(imageDataOnly) {
            return url.replace(IMAGE_URL_PREFIX, '');
        } else {
            return url;
        }
    }

    return new Promise(function(resolve, reject) {
        Plotly.plot(clonedGd, data, layoutImage, configImage)
            .then(redrawFunc)
            .then(wait)
            .then(convert)
            .then(function(url) { resolve(urlToImageData(url)); })
            .catch(function(err) { reject(err); });
    });
}

module.exports = toImage;
<commit_msg>rename isBadlySet -> isImpliedOrValid<commit_after>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var Plotly = require('../plotly');
var Lib = require('../lib');

var helpers = require('../snapshot/helpers');
var toSVG = require('../snapshot/tosvg');
var svgToImg = require('../snapshot/svgtoimg');

var getGraphDiv = require('./helpers').getGraphDiv;

var attrs = {
    format: {
        valType: 'enumerated',
        values: ['png', 'jpeg', 'webp', 'svg'],
        dflt: 'png',
        description: 'Sets the format of exported image.'
    },
    width: {
        valType: 'number',
        min: 1,
        description: [
            'Sets the exported image width.',
            'Defaults to the value found in `layout.width`'
        ].join(' ')
    },
    height: {
        valType: 'number',
        min: 1,
        description: [
            'Sets the exported image height.',
            'Defaults to the value found in `layout.height`'
        ].join(' ')
    },
    setBackground: {
        valType: 'any',
        dflt: false,
        description: [
            'Sets the image background mode.',
            'By default, the image background is determined by `layout.paper_bgcolor`,',
            'the *transparent* mode.',
            'One might consider setting `setBackground` to *opaque*',
            'when exporting a *jpeg* image as JPEGs do not support opacity.'
        ].join(' ')
    },
    imageDataOnly: {
        valType: 'boolean',
        dflt: false,
        description: [
            'Determines whether or not the return value is prefixed by',
            'the image format\'s corresponding \'data:image;\' spec.'
        ].join(' ')
    }
};

var IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;

/** Plotly.toImage
 *
 * @param {object | string | HTML div} gd
 *   can either be a data/layout/config object
 *   or an existing graph

 *   or an id to an existing graph

 * @param {object} opts (see above)
 * @return {promise}
 */
function toImage(gd, opts) {
    opts = opts || {};

    var data;
    var layout;
    var config;

    if(Lib.isPlainObject(gd)) {
        data = gd.data || [];
        layout = gd.layout || {};
        config = gd.config || {};
    } else {
        gd = getGraphDiv(gd);
        data = Lib.extendDeep([], gd.data);
        layout = Lib.extendDeep({}, gd.layout);
        config = gd._context;
    }

    function isImpliedOrValid(attr) {
        return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
    }

    if(!isImpliedOrValid('width') || !isImpliedOrValid('height')) {
        throw new Error('Height and width should be pixel values.');
    }

    if(!isImpliedOrValid('format')) {
        throw new Error('Image format is not jpeg, png, svg or webp.');
    }

    var fullOpts = {};

    function coerce(attr, dflt) {
        return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
    }

    var format = coerce('format');
    var width = coerce('width');
    var height = coerce('height');
    var setBackground = coerce('setBackground');
    var imageDataOnly = coerce('imageDataOnly');

    // put the cloned div somewhere off screen before attaching to DOM
    var clonedGd = document.createElement('div');
    clonedGd.style.position = 'absolute';
    clonedGd.style.left = '-5000px';
    document.body.appendChild(clonedGd);

    // extend layout with image options
    var layoutImage = Lib.extendFlat({}, layout);
    if(width) layoutImage.width = width;
    if(height) layoutImage.height = height;

    // extend config for static plot
    var configImage = Lib.extendFlat({}, config, {
        staticPlot: true,
        plotGlPixelRatio: config.plotGlPixelRatio || 2,
        setBackground: setBackground
    });

    var redrawFunc = helpers.getRedrawFunc(clonedGd);

    function wait() {
        return new Promise(function(resolve) {
            setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
        });
    }

    function convert() {
        return new Promise(function(resolve, reject) {
            var svg = toSVG(clonedGd);

            if(format === 'svg' && imageDataOnly) {
                return resolve(svg);
            }

            var canvas = document.createElement('canvas');
            canvas.id = Lib.randstr();

            svgToImg({
                format: format,
                width: clonedGd._fullLayout.width,
                height: clonedGd._fullLayout.height,
                canvas: canvas,
                svg: svg,
                // ask svgToImg to return a Promise
                //  rather than EventEmitter
                //  leave EventEmitter for backward
                //  compatibility
                promise: true
            })
            .then(function(url) {
                Plotly.purge(clonedGd);
                document.body.removeChild(clonedGd);
                resolve(url);
            })
            .catch(function(err) {
                reject(err);
            });
        });
    }

    function urlToImageData(url) {
        if(imageDataOnly) {
            return url.replace(IMAGE_URL_PREFIX, '');
        } else {
            return url;
        }
    }

    return new Promise(function(resolve, reject) {
        Plotly.plot(clonedGd, data, layoutImage, configImage)
            .then(redrawFunc)
            .then(wait)
            .then(convert)
            .then(function(url) { resolve(urlToImageData(url)); })
            .catch(function(err) { reject(err); });
    });
}

module.exports = toImage;
<|endoftext|>


<commit_before>const Plugin = require('../core/Plugin')
const { toArray } = require('../core/Utils')
const Translator = require('../core/Translator')
const { h } = require('preact')

module.exports = class FileInput extends Plugin {
  constructor (uppy, opts) {
    super(uppy, opts)
    this.id = this.opts.id || 'FileInput'
    this.title = 'File Input'
    this.type = 'acquirer'

    const defaultLocale = {
      strings: {
        chooseFiles: 'Choose files'
      }
    }

    // Default options
    const defaultOptions = {
      target: null,
      pretty: true,
      inputName: 'files[]',
      locale: defaultLocale
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    // i18n
    this.translator = new Translator({locale: this.locale})
    this.i18n = this.translator.translate.bind(this.translator)

    this.render = this.render.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleClick = this.handleClick.bind(this)
  }

  handleInputChange (ev) {
    this.uppy.log('[FileInput] Something selected through input...')

    const files = toArray(ev.target.files)

    files.forEach((file) => {
      this.uppy.addFile({
        source: this.id,
        name: file.name,
        type: file.type,
        data: file
      })
    })
  }

  handleClick (ev) {
    this.input.click()
  }

  render (state) {
    /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
    const hiddenInputStyle = {
      width: '0.1px',
      height: '0.1px',
      opacity: 0,
      overflow: 'hidden',
      position: 'absolute',
      zIndex: -1
    }

    const restrictions = this.uppy.opts.restrictions

    // empty value="" on file input, so that the input is cleared after a file is selected,
    // because Uppy will be handling the upload and so we can select same file
    // after removing — otherwise browser thinks it’s already selected
    return

      <input class="uppy-FileInput-input"
        style={this.opts.pretty && hiddenInputStyle}
        type="file"
        name={this.opts.inputName}
        onchange={this.handleInputChange}
        multiple={restrictions.maxNumberOfFiles !== 1}
        accept={restrictions.allowedFileTypes}
        ref={(input) => { this.input = input }} />
        value="" />
      {this.opts.pretty &&
        <button class="uppy-FileInput-btn" type="button" onclick={this.handleClick}>
          {this.i18n('chooseFiles')}
        </button>
      }

  }

  install () {
    const target = this.opts.target
    if (target) {
      this.mount(target, this)
    }
  }

  uninstall () {
    this.unmount()
  }
}
<commit_msg>fix closed tag<commit_after>const Plugin = require('../core/Plugin')
const { toArray } = require('../core/Utils')
const Translator = require('../core/Translator')
const { h } = require('preact')

module.exports = class FileInput extends Plugin {
  constructor (uppy, opts) {
    super(uppy, opts)
    this.id = this.opts.id || 'FileInput'
    this.title = 'File Input'
    this.type = 'acquirer'

    const defaultLocale = {
      strings: {
        chooseFiles: 'Choose files'
      }
    }

    // Default options
    const defaultOptions = {
      target: null,
      pretty: true,
      inputName: 'files[]',
      locale: defaultLocale
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    // i18n
    this.translator = new Translator({locale: this.locale})
    this.i18n = this.translator.translate.bind(this.translator)

    this.render = this.render.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleClick = this.handleClick.bind(this)
  }

  handleInputChange (ev) {
    this.uppy.log('[FileInput] Something selected through input...')

    const files = toArray(ev.target.files)

    files.forEach((file) => {
      this.uppy.addFile({
        source: this.id,
        name: file.name,
        type: file.type,
        data: file
      })
    })
  }

  handleClick (ev) {
    this.input.click()
  }

  render (state) {
    /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
    const hiddenInputStyle = {
      width: '0.1px',
      height: '0.1px',
      opacity: 0,
      overflow: 'hidden',
      position: 'absolute',
      zIndex: -1
    }

    const restrictions = this.uppy.opts.restrictions

    // empty value="" on file input, so that the input is cleared after a file is selected,
    // because Uppy will be handling the upload and so we can select same file
    // after removing — otherwise browser thinks it’s already selected
    return

      <input class="uppy-FileInput-input"
        style={this.opts.pretty && hiddenInputStyle}
        type="file"
        name={this.opts.inputName}
        onchange={this.handleInputChange}
        multiple={restrictions.maxNumberOfFiles !== 1}
        accept={restrictions.allowedFileTypes}
        ref={(input) => { this.input = input }}
        value="" />
      {this.opts.pretty &&
        <button class="uppy-FileInput-btn" type="button" onclick={this.handleClick}>
          {this.i18n('chooseFiles')}
        </button>
      }

  }

  install () {
    const target = this.opts.target
    if (target) {
      this.mount(target, this)
    }
  }

  uninstall () {
    this.unmount()
  }
}
<|endoftext|>