<commit_before>
import {createStructuredSelector} from 'reselect'
import React, {PropTypes, Component} from 'react'
import {connect} from './connect'

import * as actions from '../actions'

import urlParser from '../util/url'
import navigation from '../util/navigation'

import staticTabData from '../constants/static-tab-data'

import querystring from 'querystring'
import ospath from 'path'

const injectPath = ospath.resolve(__dirname, '..', 'inject', 'browser.js')
// const DONT_SHOW_WEBVIEWS = process.env.ITCH_DONT_SHOW_WEBVIEWS === '1'
const SHOW_DEVTOOLS = parseInt(process.env.DEVTOOLS, 10) > 1
const WILL_NAVIGATE_GRACE_PERIOD = 3000

import BrowserBar from './browser-bar'

import GameBrowserContext from './game-browser-context'

import {transformUrl} from '../util/navigation'

// updated when switching accounts
let currentSession = null

export class BrowserMeat extends Component {
  constructor () {
    super()
    this.state = {
      browserState: {
        canGoBack: false,
        canGoForward: false,
        loading: true,
        url: ''
      }
    }

    this.goBack = ::this.goBack
    this.goForward = ::this.goForward
    this.reload = ::this.reload
    this.stop = ::this.stop
    this.openDevTools = ::this.openDevTools
    this.loadURL = ::this.loadURL
  }

  updateBrowserState (props = {}) {
    const {webview} = this
    if (!webview) {
      return
    }
    if (!webview.partition || webview.partition === '') {
      console.warn(`${this.props.tabId}: webview has empty partition`)
    }

    const browserState = {
      ...this.state.browserState,
      canGoBack: webview.canGoBack(),
      canGoForward: webview.canGoForward(),
      ...props
    }

    this.setState({browserState})
  }

  domReady () {
    const {url} = this.props
    const {webview} = this

    const webContents = webview.getWebContents()
    if (!webContents || webContents.isDestroyed()) return

    if (SHOW_DEVTOOLS) {
      webContents.openDevTools({detach: true})
    }

    this.updateBrowserState({loading: false})

    if (currentSession !== webContents.session) {
      this.setupItchInternal(webContents.session)
    }

    if (url && url !== 'about:blank') {
      this.loadURL(url)
    }
  }

  didStartLoading () {
    this.updateBrowserState({loading: true})
  }

  didStopLoading () {
    this.updateBrowserState({loading: false})
  }

  pageTitleUpdated (e) {
    const {tabId, tabDataFetched} = this.props
    tabDataFetched(tabId, {webTitle: e.title})
  }

  pageFaviconUpdated (e) {
    const {tabId, tabDataFetched} = this.props
    tabDataFetched(tabId, {webFavicon: e.favicons[0]})
  }

  didNavigate (e) {
    const {tabId} = this.props
    const {url} = e
    this.updateBrowserState({url})
    this.analyzePage(tabId, url)
  }

  willNavigate (e) {
    if (!this.isFrozen()) {
      return
    }

    const {navigate} = this.props
    const {url} = e

    // sometimes we get double will-navigate events because life is fun?!
    if (this.lastNavigationUrl === url && e.timeStamp - this.lastNavigationTimeStamp < WILL_NAVIGATE_GRACE_PERIOD) {
      this.with((wv) => {
        wv.stop()
        wv.loadURL(this.props.url)
      })
      return
    }
    this.lastNavigationUrl = url
    this.lastNavigationTimeStamp = e.timeStamp

    navigate(`url/${url}`)

    // our own little preventDefault
    // cf. https://github.com/electron/electron/issues/1378
    this.with((wv) => {
      wv.stop()
      wv.loadURL(this.props.url)
    })
  }

  newWindow (e) {
    const {navigate} = this.props
    const {url} = e
    navigate('url/' + url)
  }

  isFrozen (e) {
    const {tabId} = this.props
    const frozen = staticTabData[tabId] || !tabId
    return frozen
  }

  setupItchInternal (session) {
    currentSession = session

    // requests to 'itch-internal' are used to communicate between web content & the app
    let internalFilter = {
      urls: ['https://itch-internal/*']
    }

    session.webRequest.onBeforeRequest(internalFilter, (details, callback) => {
      callback({cancel: true})

      let parsed = urlParser.parse(details.url)
      const {pathname, query} = parsed
      const params = querystring.parse(query)
      const {tabId} = params

      switch (pathname) {
        case '/open-devtools':
          const {webview} = this
          if (webview && webview.getWebContents() && !webview.getWebContents().isDestroyed()) {
            webview.getWebContents().openDevTools({detach: true})
          }
          break
        case '/analyze-page':
          this.analyzePage(tabId, params.url)
          break
        case '/evolve-tab':
          const {evolveTab} = this.props
          evolveTab(tabId, params.path)
          break
      }
    })
  }

  analyzePage (tabId, url) {
    const {evolveTab} = this.props

    const xhr = new window.XMLHttpRequest()
    xhr.responseType = 'document'
    xhr.onreadystatechange = () => {
      if (xhr.readyState !== 4) {
        return
      }
      const meta = xhr.responseXML.querySelector('meta[name="itch:path"]')
      if (meta) {
        const newPath = meta.content
        evolveTab(tabId, newPath)
      }
    }
    xhr.open('GET', url)

    // itch.io pages don't have CORS, but this code doesn't run in
    // a webview so CSP doesn't apply to us.
    xhr.send()
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.url) {
      const {webview} = this
      if (!webview) {
        return
      }
      if (webview.src === '' || webview.src === 'about:blank') {
        // we didn't have a proper url but now do
        this.loadURL(nextProps.url)
      }
    }
  }

  componentDidMount () {
    const {webviewShell} = this.refs

    // cf. https://github.com/electron/electron/issues/6046
    webviewShell.innerHTML = '<webview style="display: flex; flex: 1 1;"/>'
    const wv = webviewShell.querySelector('webview')
    this.webview = wv

    const {meId} = this.props
    const partition = `persist:itchio-${meId}`

    wv.partition = partition
    wv.plugins = true
    wv.preload = injectPath

    const callbackSetup = () => {
      wv.addEventListener('did-start-loading', ::this.didStartLoading)
      wv.addEventListener('did-stop-loading', ::this.didStopLoading)
      wv.addEventListener('will-navigate', ::this.willNavigate)
      wv.addEventListener('did-navigate', ::this.didNavigate)
      wv.addEventListener('page-title-updated', ::this.pageTitleUpdated)
      wv.addEventListener('page-favicon-updated', ::this.pageFaviconUpdated)
      wv.addEventListener('new-window', ::this.newWindow)
      this.domReady()

      // otherwise, back button is active and brings us back to 'about:blank'
      wv.clearHistory()
      wv.removeEventListener('dom-ready', callbackSetup)
    }
    wv.addEventListener('dom-ready', callbackSetup)

    const {tabId} = this.props
    wv.addEventListener('dom-ready', () => {
      wv.executeJavaScript(`window.__itchInit && window.__itchInit(${JSON.stringify(tabId)})`)
      this.didStopLoading()
    })

    wv.src = 'about:blank'
  }

  render () {
    const {tabData, tabPath, controls} = this.props
    const {browserState} = this.state

    const {goBack, goForward, stop, reload, openDevTools, loadURL} = this
    const controlProps = {tabPath, tabData, browserState, goBack, goForward, stop, reload, openDevTools, loadURL}

    let context = ''
    if (controls === 'game') {
      context = <GameBrowserContext {...controlProps}/>
    }

    return

      <BrowserBar {...controlProps}/>

        {context}

  }

  with (cb, opts = {insist: false}) {
    const {webview} = this
    if (!webview) return

    const webContents = webview.getWebContents()
    if (!webContents) {
      return
    }

    if (webContents.isDestroyed()) return

    cb(webview, webContents)
  }

  openDevTools () {
    this.with((wv, wc) => wc.openDevTools({detach: true}))
  }

  stop () {
    this.with((wv) => wv.stop())
  }

  reload () {
    this.with((wv) => {
      wv.reload()
    })
    const {tabId, tabReloaded} = this.props
    tabReloaded(tabId)
  }

  goBack () {
    this.with((wv) => wv.goBack())
  }

  goForward () {
    this.with((wv) => wv.goForward())
  }

  async loadURL (input) {
    const {navigate} = this.props
    const url = await transformUrl(input)

    if (navigation.isAppSupported(url) && this.isFrozen()) {
      navigate(`url/${url}`)
    } else {
      const browserState = {...this.state.browserState, url}
      this.setState({browserState})

      const {webview} = this
      if (webview) {
        webview.loadURL(url)
      }
    }
  }
}

BrowserMeat.propTypes = {
  url: PropTypes.string.isRequired,
  tabPath: PropTypes.string,
  tabData: PropTypes.object,
  tabId: PropTypes.string.isRequired,
  className: PropTypes.string,
  meId: PropTypes.any,
  navigate: PropTypes.any,

  tabReloaded: PropTypes.func.isRequired,
  evolveTab: PropTypes.func.isRequired,

  controls: PropTypes.oneOf(['generic', 'game', 'user'])
}

const mapStateToProps = createStructuredSelector({
  meId: (state) => (state.session.credentials.me || {id: 'anonymous'}).id
})

const mapDispatchToProps = (dispatch) => ({
  navigate: (id, data) => dispatch(actions.navigate(id, data)),
  evolveTab: (id, path) => dispatch(actions.evolveTab({id, path})),
  tabDataFetched: (id, data) => dispatch(actions.tabDataFetched({id, data, timestamp: +new Date()})),
  tabReloaded: (id) => dispatch(actions.tabReloaded({id}))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(BrowserMeat)
<commit_msg>hide browser controls in itch.io tab<commit_after>
import {createStructuredSelector} from 'reselect'
import React, {PropTypes, Component} from 'react'
import {connect} from './connect'

import * as actions from '../actions'

import urlParser from '../util/url'
import navigation from '../util/navigation'

import staticTabData from '../constants/static-tab-data'

import querystring from 'querystring'
import ospath from 'path'

const injectPath = ospath.resolve(__dirname, '..', 'inject', 'browser.js')
// const DONT_SHOW_WEBVIEWS = process.env.ITCH_DONT_SHOW_WEBVIEWS === '1'
const SHOW_DEVTOOLS = parseInt(process.env.DEVTOOLS, 10) > 1
const WILL_NAVIGATE_GRACE_PERIOD = 3000

import BrowserBar from './browser-bar'

import GameBrowserContext from './game-browser-context'

import {transformUrl} from '../util/navigation'

// updated when switching accounts
let currentSession = null

export class BrowserMeat extends Component {
  constructor () {
    super()
    this.state = {
      browserState: {
        canGoBack: false,
        canGoForward: false,
        loading: true,
        url: ''
      }
    }

    this.goBack = ::this.goBack
    this.goForward = ::this.goForward
    this.reload = ::this.reload
    this.stop = ::this.stop
    this.openDevTools = ::this.openDevTools
    this.loadURL = ::this.loadURL
  }

  updateBrowserState (props = {}) {
    const {webview} = this
    if (!webview) {
      return
    }
    if (!webview.partition || webview.partition === '') {
      console.warn(`${this.props.tabId}: webview has empty partition`)
    }

    const browserState = {
      ...this.state.browserState,
      canGoBack: webview.canGoBack(),
      canGoForward: webview.canGoForward(),
      ...props
    }

    this.setState({browserState})
  }

  domReady () {
    const {url} = this.props
    const {webview} = this

    const webContents = webview.getWebContents()
    if (!webContents || webContents.isDestroyed()) return

    if (SHOW_DEVTOOLS) {
      webContents.openDevTools({detach: true})
    }

    this.updateBrowserState({loading: false})

    if (currentSession !== webContents.session) {
      this.setupItchInternal(webContents.session)
    }

    if (url && url !== 'about:blank') {
      this.loadURL(url)
    }
  }

  didStartLoading () {
    this.updateBrowserState({loading: true})
  }

  didStopLoading () {
    this.updateBrowserState({loading: false})
  }

  pageTitleUpdated (e) {
    const {tabId, tabDataFetched} = this.props
    tabDataFetched(tabId, {webTitle: e.title})
  }

  pageFaviconUpdated (e) {
    const {tabId, tabDataFetched} = this.props
    tabDataFetched(tabId, {webFavicon: e.favicons[0]})
  }

  didNavigate (e) {
    const {tabId} = this.props
    const {url} = e
    this.updateBrowserState({url})
    this.analyzePage(tabId, url)
  }

  willNavigate (e) {
    if (!this.isFrozen()) {
      return
    }

    const {navigate} = this.props
    const {url} = e

    // sometimes we get double will-navigate events because life is fun?!
    if (this.lastNavigationUrl === url && e.timeStamp - this.lastNavigationTimeStamp < WILL_NAVIGATE_GRACE_PERIOD) {
      this.with((wv) => {
        wv.stop()
        wv.loadURL(this.props.url)
      })
      return
    }
    this.lastNavigationUrl = url
    this.lastNavigationTimeStamp = e.timeStamp

    navigate(`url/${url}`)

    // our own little preventDefault
    // cf. https://github.com/electron/electron/issues/1378
    this.with((wv) => {
      wv.stop()
      wv.loadURL(this.props.url)
    })
  }

  newWindow (e) {
    const {navigate} = this.props
    const {url} = e
    navigate('url/' + url)
  }

  isFrozen (e) {
    const {tabId} = this.props
    const frozen = staticTabData[tabId] || !tabId
    return frozen
  }

  setupItchInternal (session) {
    currentSession = session

    // requests to 'itch-internal' are used to communicate between web content & the app
    let internalFilter = {
      urls: ['https://itch-internal/*']
    }

    session.webRequest.onBeforeRequest(internalFilter, (details, callback) => {
      callback({cancel: true})

      let parsed = urlParser.parse(details.url)
      const {pathname, query} = parsed
      const params = querystring.parse(query)
      const {tabId} = params

      switch (pathname) {
        case '/open-devtools':
          const {webview} = this
          if (webview && webview.getWebContents() && !webview.getWebContents().isDestroyed()) {
            webview.getWebContents().openDevTools({detach: true})
          }
          break
        case '/analyze-page':
          this.analyzePage(tabId, params.url)
          break
        case '/evolve-tab':
          const {evolveTab} = this.props
          evolveTab(tabId, params.path)
          break
      }
    })
  }

  analyzePage (tabId, url) {
    const {evolveTab} = this.props

    const xhr = new window.XMLHttpRequest()
    xhr.responseType = 'document'
    xhr.onreadystatechange = () => {
      if (xhr.readyState !== 4) {
        return
      }
      const meta = xhr.responseXML.querySelector('meta[name="itch:path"]')
      if (meta) {
        const newPath = meta.content
        evolveTab(tabId, newPath)
      }
    }
    xhr.open('GET', url)

    // itch.io pages don't have CORS, but this code doesn't run in
    // a webview so CSP doesn't apply to us.
    xhr.send()
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.url) {
      const {webview} = this
      if (!webview) {
        return
      }
      if (webview.src === '' || webview.src === 'about:blank') {
        // we didn't have a proper url but now do
        this.loadURL(nextProps.url)
      }
    }
  }

  componentDidMount () {
    const {webviewShell} = this.refs

    // cf. https://github.com/electron/electron/issues/6046
    webviewShell.innerHTML = '<webview style="display: flex; flex: 1 1;"/>'
    const wv = webviewShell.querySelector('webview')
    this.webview = wv

    const {meId} = this.props
    const partition = `persist:itchio-${meId}`

    wv.partition = partition
    wv.plugins = true
    wv.preload = injectPath

    const callbackSetup = () => {
      wv.addEventListener('did-start-loading', ::this.didStartLoading)
      wv.addEventListener('did-stop-loading', ::this.didStopLoading)
      wv.addEventListener('will-navigate', ::this.willNavigate)
      wv.addEventListener('did-navigate', ::this.didNavigate)
      wv.addEventListener('page-title-updated', ::this.pageTitleUpdated)
      wv.addEventListener('page-favicon-updated', ::this.pageFaviconUpdated)
      wv.addEventListener('new-window', ::this.newWindow)
      this.domReady()

      // otherwise, back button is active and brings us back to 'about:blank'
      wv.clearHistory()
      wv.removeEventListener('dom-ready', callbackSetup)
    }
    wv.addEventListener('dom-ready', callbackSetup)

    const {tabId} = this.props
    wv.addEventListener('dom-ready', () => {
      wv.executeJavaScript(`window.__itchInit && window.__itchInit(${JSON.stringify(tabId)})`)
      this.didStopLoading()
    })

    wv.src = 'about:blank'
  }

  render () {
    const {tabData, tabPath, controls} = this.props
    const {browserState} = this.state

    const {goBack, goForward, stop, reload, openDevTools, loadURL} = this
    const controlProps = {tabPath, tabData, browserState, goBack, goForward, stop, reload, openDevTools, loadURL}

    let context = ''
    if (controls === 'game') {
      context = <GameBrowserContext {...controlProps}/>
    }

    return

      {this.isFrozen()
        ? ''
        : <BrowserBar {...controlProps}/>
      }

        {context}

  }

  with (cb, opts = {insist: false}) {
    const {webview} = this
    if (!webview) return

    const webContents = webview.getWebContents()
    if (!webContents) {
      return
    }

    if (webContents.isDestroyed()) return

    cb(webview, webContents)
  }

  openDevTools () {
    this.with((wv, wc) => wc.openDevTools({detach: true}))
  }

  stop () {
    this.with((wv) => wv.stop())
  }

  reload () {
    this.with((wv) => {
      wv.reload()
    })
    const {tabId, tabReloaded} = this.props
    tabReloaded(tabId)
  }

  goBack () {
    this.with((wv) => wv.goBack())
  }

  goForward () {
    this.with((wv) => wv.goForward())
  }

  async loadURL (input) {
    const {navigate} = this.props
    const url = await transformUrl(input)

    if (navigation.isAppSupported(url) && this.isFrozen()) {
      navigate(`url/${url}`)
    } else {
      const browserState = {...this.state.browserState, url}
      this.setState({browserState})

      const {webview} = this
      if (webview) {
        webview.loadURL(url)
      }
    }
  }
}

BrowserMeat.propTypes = {
  url: PropTypes.string.isRequired,
  tabPath: PropTypes.string,
  tabData: PropTypes.object,
  tabId: PropTypes.string.isRequired,
  className: PropTypes.string,
  meId: PropTypes.any,
  navigate: PropTypes.any,

  tabReloaded: PropTypes.func.isRequired,
  evolveTab: PropTypes.func.isRequired,

  controls: PropTypes.oneOf(['generic', 'game', 'user'])
}

const mapStateToProps = createStructuredSelector({
  meId: (state) => (state.session.credentials.me || {id: 'anonymous'}).id
})

const mapDispatchToProps = (dispatch) => ({
  navigate: (id, data) => dispatch(actions.navigate(id, data)),
  evolveTab: (id, path) => dispatch(actions.evolveTab({id, path})),
  tabDataFetched: (id, data) => dispatch(actions.tabDataFetched({id, data, timestamp: +new Date()})),
  tabReloaded: (id) => dispatch(actions.tabReloaded({id}))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(BrowserMeat)
<|endoftext|>


<commit_before>/*
 * Copyright [1999-2014] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function($) {
  var rows_per_subtable = 1000;

  function beat(def,sleeplen) {
    return def.then(function(data) {
      var d = $.Deferred();
      setTimeout(function() { d.resolve(data); },sleeplen);
      return d;
    });
  }

  function loop(def,fn,group,sleeplen) {
    return def.then(function(raw_input) {
      var input = [];
      $.each(raw_input,function(a,b) { input.push([a,b]); });
      d = $.Deferred().resolve(input);
      var output = [];
      for(var ii=0;ii<input.length;ii+=group) {
        (function(i) {
          d = beat(d.then(function(j) {
            for(j=0;j<group && i+j<input.length;j++) {
              var c = fn(input[i+j][0],input[i+j][1]);
              if(c !== undefined) {
                output.push(c);
              }
            }
            return $.Deferred().resolve(output);
          }),sleeplen);
        })(ii);
      }
      return d;
    });
  }

  function new_th(index,colconf,$header) {
    var text = colconf.label || colconf.title || colconf.key;
    var attrs = {};
    var classes = [];
    attrs['data-key'] = colconf.key || '';
    if(colconf.width) { attrs.width = colconf.width; }
    if(colconf.sort)  { classes.push('sorting'); }
    if(colconf.help) {
      var help = $('
').text(colconf.help).html();
      text =
        ''+text+'';
    }
    var attr_str = "";
    $.each(attrs,function(k,v) {
      attr_str += ' '+k+'="'+v+'"';
    });
    if(classes.length) {
      attr_str += ' class="'+classes.join(' ')+'"';
    };
    return ""+text+"	";
  }

  function new_header(config) {
    var columns = [];

    var $header = $('

');
    $.each(config.columns,function(i,data) {
      columns.push(new_th(i,data,$header));
    });
    return '

'+columns.join('')+"

";
  }

  function add_sort($table,key,clear) {
    // Update data
    var view = $table.data('view');
    var sort = [];
    if(view && view.sort) { sort = view.sort; }
    var new_sort = [];
    var dir = 0;
    $.each(sort,function(i,val) {
      if(val.key == key) {
        dir = -val.dir;
      } else if(!clear) {
        new_sort.push(val);
      }
    });
    if(!dir) { dir = 1; }
    new_sort.push({ key: key, dir: dir });
    view.sort = new_sort;
    $table.data('view',view);
    $table.trigger('view-updated');
    // Reflect data in display
    $('th',$table).removeClass('sorting_asc').removeClass('sorting_desc');
    $.each(new_sort,function(i,val) {
      var dir = val.dir>0?'asc':'desc';
      $('th[data-key="'+val.key+'"]').addClass('sorting_'+dir);
    });
  }

  function new_subtable($table) {
    return $("

");
  }

  function extend_rows($table,rows) {
    var $thead = $('thead',$table);
    var $tbody = $('tbody',$table);
    var nrows = $('tr',$tbody).length;
    var ncols = $('th',$thead).length;
    var row = "
";
    for(var i=0;i<ncols;i++) {
      row += "	";
    }
    row += "
";
    var target = rows[1];
    var $subtables = $('table',$table);
    for(var i=0;i<$subtables.length;i++) {
      var $subtable;
      if(i+1 >= $subtables.length) {
        $subtable = new_subtable($table).appendTo($('.new_table',$table));
      } else {
        $subtable = $subtables.eq(i+1);
      }
      var nsrows = $('tbody tr',$subtable).length;
      target-= nsrows;
    }
    for(var i=0;i<$subtables.length;i++) {
      var $subtable = $subtables.eq(i+1);
      var nsrows = $('tbody tr',$subtable).length;
      if(nsrows < rows_per_subtable) {
        var to_add = target;
        if(to_add+nsrows > rows_per_subtable)
          to_add = rows_per_subtable - nsrows;
        var html = "";
        for(var j=0;j<to_add;j++) { html += row; }
        $subtable.append(html);
        target -= to_add;
      }
    }
  }

  function update_row2($table,data,row,columns) {
    var table_num = Math.floor(row/rows_per_subtable);
    var $subtable = $('table',$table).eq(table_num+1);
    var markup = $subtable.data('markup') || [];
    var idx = row-table_num*rows_per_subtable;
    markup[idx] = markup[idx] || [];
    di = 0;
    for(var i=0;i<columns.length;i++) {
      if(columns[i])
        markup[idx][i] = data[di++];
    }
    $subtable.data('markup',markup);
    return table_num;
  }

  function update_row3($table,row) {
    var table_num = Math.floor(row/rows_per_subtable);
    var $subtable = $('table',$table).eq(table_num+1);
    var markup = $subtable.data('markup') || [];
    var html = "";
    for(var i=0;i<markup.length;i++) {
      html += "
";
      for(var j=0;j<markup[i].length;j++) {
        if(markup[i][j]) {
          html += ""+(markup[i][j])+"	";
        } else {
          html += "	";
        }
      }
      html += "
";
    }
    $('tbody',$subtable).html(html);
  }

  $.fn.new_table_tabular = function(config,data) {
    return {
      layout: function($table) {
        var config = $table.data('config');
        var header = new_header(config);
        return '

'+header+'

';
      },
      go: function($table,$el) {
        $('th',$table).click(function(e) {
          add_sort($table,$(this).data('key'),!e.shiftKey);
        });
      },
      add_data: function($table,data,rows,columns) {
        console.log("add_data");
        extend_rows($table,rows);
        var subtabs = [];
        $.each(data,function(i,val) {
          subtabs[update_row2($table,val,i+rows[0],columns)]=1;
        });
        d = $.Deferred().resolve(subtabs);
        loop(d,function(tabnum,v) {
          console.log("updating table "+tabnum);
          update_row3($table,tabnum);
        },2,0);
      }
    };
  };

})(jQuery);
<commit_msg>Bugfixes to subtable system.<commit_after>/*
 * Copyright [1999-2014] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function($) {
  var rows_per_subtable = 1000;

  function beat(def,sleeplen) {
    return def.then(function(data) {
      var d = $.Deferred();
      setTimeout(function() { d.resolve(data); },sleeplen);
      return d;
    });
  }

  function loop(def,fn,group,sleeplen) {
    return def.then(function(raw_input) {
      var input = [];
      $.each(raw_input,function(a,b) { input.push([a,b]); });
      d = $.Deferred().resolve(input);
      var output = [];
      for(var ii=0;ii<input.length;ii+=group) {
        (function(i) {
          d = beat(d.then(function(j) {
            for(j=0;j<group && i+j<input.length;j++) {
              var c = fn(input[i+j][0],input[i+j][1]);
              if(c !== undefined) {
                output.push(c);
              }
            }
            return $.Deferred().resolve(output);
          }),sleeplen);
        })(ii);
      }
      return d;
    });
  }

  function new_th(index,colconf,$header) {
    var text = colconf.label || colconf.title || colconf.key;
    var attrs = {};
    var classes = [];
    attrs['data-key'] = colconf.key || '';
    if(colconf.width) { attrs.width = colconf.width; }
    if(colconf.sort)  { classes.push('sorting'); }
    if(colconf.help) {
      var help = $('
').text(colconf.help).html();
      text =
        ''+text+'';
    }
    var attr_str = "";
    $.each(attrs,function(k,v) {
      attr_str += ' '+k+'="'+v+'"';
    });
    if(classes.length) {
      attr_str += ' class="'+classes.join(' ')+'"';
    };
    return ""+text+"	";
  }

  function new_header(config) {
    var columns = [];

    var $header = $('

');
    $.each(config.columns,function(i,data) {
      columns.push(new_th(i,data,$header));
    });
    return '

'+columns.join('')+"

";
  }

  function add_sort($table,key,clear) {
    // Update data
    var view = $table.data('view');
    var sort = [];
    if(view && view.sort) { sort = view.sort; }
    var new_sort = [];
    var dir = 0;
    $.each(sort,function(i,val) {
      if(val.key == key) {
        dir = -val.dir;
      } else if(!clear) {
        new_sort.push(val);
      }
    });
    if(!dir) { dir = 1; }
    new_sort.push({ key: key, dir: dir });
    view.sort = new_sort;
    $table.data('view',view);
    $table.trigger('view-updated');
    // Reflect data in display
    $('th',$table).removeClass('sorting_asc').removeClass('sorting_desc');
    $.each(new_sort,function(i,val) {
      var dir = val.dir>0?'asc':'desc';
      $('th[data-key="'+val.key+'"]').addClass('sorting_'+dir);
    });
  }

  function new_subtable($table) {
    return $("

");
  }

  function extend_rows($table,rows) {
    console.log("extend_rows");
    var $thead = $('thead',$table);
    var $tbody = $('tbody',$table);
    var nrows = $('tr',$tbody).length;
    var ncols = $('th',$thead).length;
    var row = "
";
    for(var i=0;i<ncols;i++) {
      row += "	";
    }
    row += "
";
    var target = rows[1];
    var $subtables = $('table',$table);
    for(var i=0;i<$subtables.length-1;i++) {
      var $subtable = $subtables.eq(i+1);
      var nsrows = $('tbody tr',$subtable).length;
      if(nsrows < rows_per_subtable && target>0) {
        var to_add = target;
        if(nsrows+to_add > rows_per_subtable) {
          to_add = rows_per_subtable - nsrows;
        }
        var html = "";
        for(var j=nsrows;j<rows_per_subtable;j++) { html += row; }
        $subtable.append(html);
        ns_rows = rows_per_subtable;
      }
      target -= nsrows;
    }
    while(target > 0) {
      var $subtable = new_subtable($table).appendTo($('.new_table',$table));
      var to_add = target;
      if(to_add > rows_per_subtable)
        to_add = rows_per_subtable;
      var html = "";
      for(var j=0;j<to_add;j++) { html += row; }
      $subtable.append(html);
      target -= to_add;
    }
    console.log("/extend_rows");
  }

  function update_row2($table,data,row,columns) {
    var table_num = Math.floor(row/rows_per_subtable);
    var $subtable = $('table',$table).eq(table_num+1);
    var markup = $subtable.data('markup') || [];
    var idx = row-table_num*rows_per_subtable;
    markup[idx] = markup[idx] || [];
    di = 0;
    for(var i=0;i<columns.length;i++) {
      if(columns[i])
        markup[idx][i] = data[di++];
    }
    $subtable.data('markup',markup);
    return table_num;
  }

  function update_row3($table,table_num) {
    console.log("update_row3");
    var $subtable = $('table',$table).eq(table_num+1);
    var markup = $subtable.data('markup') || [];
    var html = "";
    for(var i=0;i<markup.length;i++) {
      html += "
";
      for(var j=0;j<markup[i].length;j++) {
        if(markup[i][j]) {
          html += ""+(markup[i][j])+"	";
        } else {
          html += "	";
        }
      }
      html += "
";
    }
    $('tbody',$subtable).html(html);
  }

  $.fn.new_table_tabular = function(config,data) {
    return {
      layout: function($table) {
        var config = $table.data('config');
        var header = new_header(config);
        return '

'+header+'

';
      },
      go: function($table,$el) {
        $('th',$table).click(function(e) {
          add_sort($table,$(this).data('key'),!e.shiftKey);
        });
      },
      add_data: function($table,data,rows,columns) {
        console.log("add_data");
        extend_rows($table,rows);
        var subtabs = [];
        $.each(data,function(i,val) {
          subtabs[update_row2($table,val,i+rows[0],columns)]=1;
        });
        d = $.Deferred().resolve(subtabs);
        loop(d,function(tabnum,v) {
          update_row3($table,tabnum);
        },1,100);
      }
    };
  };

})(jQuery);
<|endoftext|>


<commit_before>var year = new Date().getFullYear();
var month = new Date().getMonth();
var day = new Date().getDate();
var CURRENT_PAGE = window.location.pathname;
var add_to = '+ add to schedule'
var remove_from = '- remove from schedule';

function init() {
    $('#calendar').weekCalendar({
        businessHours: {
            start: 8,
            end: 24,
            limitDisplay: true
        },
        timeslotsPerHour: 2,
        daysToShow: 5,
        timeSeparator: '–',
        readonly: true,
        allowCalEventOverlap: true,
        overlapEventsSeparate: true,
        height: function($calendar) {
            return $('table.wc-time-slots').height() + $('#calendar').find(".wc-toolbar").outerHeight() + $('#calendar').find(".wc-header").outerHeight();
        },
        eventHeader: function(calEvent, calendar) {
            // var options = calendar.weekCalendar('option');
            //             var one_hour = 3600000;
            //             var displayTitleWithTime = calEvent.end.getTime() - calEvent.start.getTime() <= (2 * (one_hour / options.timeslotsPerHour));
            //             if (displayTitleWithTime) {
            //                 return calEvent.title;
            //             }
            //             else {
            //                 return calendar.weekCalendar('formatTime', calEvent.start, options.timeFormat) + options.timeSeparator + calendar.weekCalendar('formatTime', calEvent.end, options.timeFormat);
            //             }
            return calEvent.title;
        },
        eventBody: function(calEvent, calendar) {
            // var options = calendar.weekCalendar('option');
            //             var one_hour = 3600000;
            //             var displayTitleWithTime = calEvent.end.getTime() - calEvent.start.getTime() <= (2 * (one_hour / options.timeslotsPerHour));
            //             if (displayTitleWithTime) {
            //                 return '';
            //             }
            //             else {
            //                 return calEvent.title;
            //             }
            return '';
        },
        eventRender: function(calEvent, $event) {
            if (calEvent.end.getTime() < new Date().getTime()) {
                $event.css("backgroundColor", "#aaa");
                $event.find(".time").css({
                    "backgroundColor": "#999",
                    "border": "1px solid #888"
                });
            }
        },
        eventAfterRender: function(calEvent, $event) {
            $event.addClass('campus_' + calEvent.campus);
        },
        getHeaderDate: function(date, $calendar) {
            var options = $calendar.weekCalendar('option');
            var dayName = options.useShortDayNames ? options.shortDays[date.getDay()] : options.longDays[date.getDay()];
            return dayName;
        },
    });

    gotodate = $('#calendar').weekCalendar('gotoDate', new Date(2012, 8, 3, 0, 0, 0, 0));
}

function addCourseData(course_data, frozen) {
    var events = course_data.events;
    var info = course_data.info;
    for (var evid in events) {
        var cevent = events[evid];
        var translated_event = {
            "start": Date.parse(cevent.start),
            "end": Date.parse(cevent.end),
            "title": cevent.title,
            "id": cevent.id,
            "campus": info.campus_code,
        };
        $('#calendar').weekCalendar('updateEvent', translated_event);
    }

    var $c_c_list = $('ol#schedule_courses');
    if (frozen === true) {
        $c_c_list.append('
' +
        info.course_code + '
');
    } else {
        $c_c_list.append('
' +
        info.course_code + 'x
');
        $('li#indicator_' + info.course_code_slug + ' a.course_delete').click(function(event) {
            removeCourse(info.course_code_slug);
        });

        var $result_entry = $('h3#course_' + info.course_code_slug);
        if ($result_entry.length != 0) {
            $result_entry.data('added', true);
            $result_entry.prev().text(remove_from);
        }
    }
}

function addCourse(course_slug) {
    $.get(CURRENT_PAGE + course_slug + '/add/', addCourseData);
}

function toggleCourse(course_slug) {
    var $c = $('h3#course_' + course_slug);
    var added = $c.data('added');
    if (added == true || added == "True") {
        removeCourseData(course_slug);
        $c.prev().text(add_to);
        $('li#indicator_' + course_slug).remove();
        $c.data('added', false);
    } else {
        $c.prev().text(remove_from);
        addCourse(course_slug);
        $c.data('added', true);
    }
}

function removeCourse(course_slug) {
    var $c = $('h3#course_' + course_slug);
    if ($c.length != 0) {
        toggleCourse(course_slug);
    } else {
        removeCourseData(course_slug);
        $('li#indicator_' + course_slug).remove();
    }
}

function removeCourseData(course_slug) {
    $.get(CURRENT_PAGE + course_slug + '/remove/',
    function(data) {
        for (var idid in data) {
            var event_id = data[idid];
            $('#calendar').weekCalendar('removeEvent', event_id);
        }
    });
}

function loadSavedCalendar(all_data) {
    for (var cid in all_data) {
        var course_data = all_data[cid];
        addCourseData(course_data, true);
    }
}

function loadCalendar() {
    $.get(CURRENT_PAGE + 'load/',
    function(data) {
        for (var cid in data) {
            var course_data = data[cid];
            addCourseData(course_data, false);
        }

        $('ol.course_list > li > h3').each(function(idx, elem) {
            var course_code_slug = $(elem).data('code');
            var already_in_calendar = $(elem).data('added');
            if (already_in_calendar) {
                var new_link = $('' + remove_from + '');
            } else {
                var new_link = $('' + add_to + '');
            }

            new_link.bind('click', function(event) {
                toggleCourse(course_code_slug);
            });
            $(elem).before(new_link);
        });

        $('h4#share_clear a#clear_schedule').click(function(e) {
            clearCalendar();
        });
    });
}

function clearCalendar() {
    $.get(CURRENT_PAGE + 'clear/',
    function(data) {
        $('#calendar').weekCalendar('clear');
        window.location.replace(CURRENT_PAGE);
    });
}
<commit_msg>Cleaned up code for the schedule event block display<commit_after>var year = new Date().getFullYear();
var month = new Date().getMonth();
var day = new Date().getDate();
var CURRENT_PAGE = window.location.pathname;
var add_to = '+ add to schedule'
var remove_from = '- remove from schedule';

function init() {
    $('#calendar').weekCalendar({
        businessHours: {
            start: 8,
            end: 24,
            limitDisplay: true
        },
        timeslotsPerHour: 2,
        daysToShow: 5,
        timeSeparator: '–',
        readonly: true,
        allowCalEventOverlap: true,
        overlapEventsSeparate: true,
        height: function($calendar) {
            return $('table.wc-time-slots').height() + $('#calendar').find(".wc-toolbar").outerHeight() + $('#calendar').find(".wc-header").outerHeight();
        },
        eventHeader: function(calEvent, calendar) {
            return calEvent.title;
        },
        eventBody: function(calEvent, calendar) {
            var options = calendar.weekCalendar('option');
            var one_hour = 3600000;
            var displayTitleWithTime = calEvent.end.getTime() - calEvent.start.getTime() <= (2 * (one_hour / options.timeslotsPerHour));
            if (displayTitleWithTime) {
                return '';
            }
            else {
                return calendar.weekCalendar('formatTime', calEvent.start, options.timeFormat) + options.timeSeparator + calendar.weekCalendar('formatTime', calEvent.end, options.timeFormat);
            }

        },
        eventRender: function(calEvent, $event) {
            if (calEvent.end.getTime() < new Date().getTime()) {
                $event.css("backgroundColor", "#aaa");
                $event.find(".time").css({
                    "backgroundColor": "#999",
                    "border": "1px solid #888"
                });
            }
        },
        eventAfterRender: function(calEvent, $event) {
            $event.addClass('campus_' + calEvent.campus);
        },
        getHeaderDate: function(date, $calendar) {
            var options = $calendar.weekCalendar('option');
            var dayName = options.useShortDayNames ? options.shortDays[date.getDay()] : options.longDays[date.getDay()];
            return dayName;
        },
    });

    gotodate = $('#calendar').weekCalendar('gotoDate', new Date(2012, 8, 3, 0, 0, 0, 0));
}

function addCourseData(course_data, frozen) {
    var events = course_data.events;
    var info = course_data.info;
    for (var evid in events) {
        var cevent = events[evid];
        var translated_event = {
            "start": Date.parse(cevent.start),
            "end": Date.parse(cevent.end),
            "title": cevent.title,
            "id": cevent.id,
            "campus": info.campus_code,
        };
        $('#calendar').weekCalendar('updateEvent', translated_event);
    }

    var $c_c_list = $('ol#schedule_courses');
    if (frozen === true) {
        $c_c_list.append('
' +
        info.course_code + '
');
    } else {
        $c_c_list.append('
' +
        info.course_code + 'x
');
        $('li#indicator_' + info.course_code_slug + ' a.course_delete').click(function(event) {
            removeCourse(info.course_code_slug);
        });

        var $result_entry = $('h3#course_' + info.course_code_slug);
        if ($result_entry.length != 0) {
            $result_entry.data('added', true);
            $result_entry.prev().text(remove_from);
        }
    }
}

function addCourse(course_slug) {
    $.get(CURRENT_PAGE + course_slug + '/add/', addCourseData);
}

function toggleCourse(course_slug) {
    var $c = $('h3#course_' + course_slug);
    var added = $c.data('added');
    if (added == true || added == "True") {
        removeCourseData(course_slug);
        $c.prev().text(add_to);
        $('li#indicator_' + course_slug).remove();
        $c.data('added', false);
    } else {
        $c.prev().text(remove_from);
        addCourse(course_slug);
        $c.data('added', true);
    }
}

function removeCourse(course_slug) {
    var $c = $('h3#course_' + course_slug);
    if ($c.length != 0) {
        toggleCourse(course_slug);
    } else {
        removeCourseData(course_slug);
        $('li#indicator_' + course_slug).remove();
    }
}

function removeCourseData(course_slug) {
    $.get(CURRENT_PAGE + course_slug + '/remove/',
    function(data) {
        for (var idid in data) {
            var event_id = data[idid];
            $('#calendar').weekCalendar('removeEvent', event_id);
        }
    });
}

function loadSavedCalendar(all_data) {
    for (var cid in all_data) {
        var course_data = all_data[cid];
        addCourseData(course_data, true);
    }
}

function loadCalendar() {
    $.get(CURRENT_PAGE + 'load/',
    function(data) {
        for (var cid in data) {
            var course_data = data[cid];
            addCourseData(course_data, false);
        }

        $('ol.course_list > li > h3').each(function(idx, elem) {
            var course_code_slug = $(elem).data('code');
            var already_in_calendar = $(elem).data('added');
            if (already_in_calendar) {
                var new_link = $('' + remove_from + '');
            } else {
                var new_link = $('' + add_to + '');
            }

            new_link.bind('click', function(event) {
                toggleCourse(course_code_slug);
            });
            $(elem).before(new_link);
        });

        $('h4#share_clear a#clear_schedule').click(function(e) {
            clearCalendar();
        });
    });
}

function clearCalendar() {
    $.get(CURRENT_PAGE + 'clear/',
    function(data) {
        $('#calendar').weekCalendar('clear');
        window.location.replace(CURRENT_PAGE);
    });
}
<|endoftext|>


<commit_before>/*!
 * SWIF-OpenSTC
 * Copyright 2013-2014 Siclic <contact@siclic.fr>
 * Licensed under AGPL-3.0 (https://www.gnu.org/licenses/agpl.txt)
 */

define([
	'app',

	'genericModalView',
	'advancedSelectBoxView',

	'interventionModel',
	'taskModel',

], function(app, GenericModalView, AdvancedSelectBoxView, InterventionModel, TaskModel){

	'use strict';

	/******************************************
	* Place Modal View
	*/
	var modalUnplanTaskView =  GenericModalView.extend({

		templateHTML: '/templates/modals/tasks/modalUnplanTask.html',

		// The DOM events //
		events: function(){
			return _.defaults({
				'click #btnRemoveTask': 'removeTaskFromSchedule'
			},
				GenericModalView.prototype.events
			);
		},

		/** View Initialization
		*/
		initialize : function(params) {
			this.options = params;

			this.modal = $(this.el);

			// Intervention Model in the Left column //
			this.interModel = this.options.interModel;

			this.render();
		},

		/** Display the view
		*/
		render : function() {
			var self = this;

			// Retrieve the template //
			$.get(app.menus.openstc+this.templateHTML, function(templateData){

				var template = _.template(templateData, {
					lang     : app.lang,
					task     : self.model,
					TaskModel: TaskModel
				});

				self.modal.html(template);
				self.modal.modal('show');
			});

			return this;
		},

		/** Remove the Task from the Calendar
		*/
		removeTaskFromSchedule: function(e){

			var self = this;
			// Set the button in loading State //
			$(e.target).button('loading');

			//Cancel absent task (no intervention)
			if(( TaskModel.status[this.model.toJSON().state].key === TaskModel.status.absent.key ) ){
				this.model.destroy({wait: true})
				.done(function(){
					self.modal.modal('hide');
				})
				.fail(function(){
					console.error(e);
				})
				.always(function(){
					// Reset the button state //
					$(e.target).button('reset');
				});
			}
			//Template task unplanned
			else if(!_.isNull(this.interModel) && !_.isUndefined(this.interModel)  && ( InterventionModel.status[this.interModel.toJSON().state].key === InterventionModel.status.template.key ) ){

				//remove template task
				this.model.destroy({wait: true})
					.done(function(){
						//re-fetch intervention
						$.when(  self.interModel.fetch() )
							.done(function(){
								self.modal.modal('hide');
							});
					})
					.fail(function(){
						console.error(e);
					})
					.always(function(){
						// Reset the button state //
						$(e.target).button('reset');
					});
			}
			//others tasks to unplanned
			else
			{
				//Set Task fields
				var params = {
					state     : TaskModel.status.draft.key,
					user_id   : false,
					team_id   : false,
					date_end  : false,
					date_start: false,
				};
				//Update task and intervention
				this.model.save(params, {patch: true, silent: false})
					.done(function() {
						var ajaxRequests = [self.model.fetch({ data : {fields : self.model.fields} } )];

						if( !_.isUndefined(self.interModel) ){
							//Add ajax request for update intervention
							ajaxRequests.push(self.interModel.fetch());
						}

						$.when( ajaxRequests )
							.done(function(){
								self.modal.modal('hide');
							})
							.fail(function(e){
								console.error(e);
							});
					})
					.always(function(){
						// Reset the button state //
						$(e.target).button('reset');
					});
			}
		}

	});

	return modalUnplanTaskView;

});<commit_msg>refs #2770 : BUG FIX Retirer un tâche planifiée à un prestataire<commit_after>/*!
 * SWIF-OpenSTC
 * Copyright 2013-2014 Siclic <contact@siclic.fr>
 * Licensed under AGPL-3.0 (https://www.gnu.org/licenses/agpl.txt)
 */

define([
	'app',

	'genericModalView',
	'advancedSelectBoxView',

	'interventionModel',
	'taskModel',

], function(app, GenericModalView, AdvancedSelectBoxView, InterventionModel, TaskModel){

	'use strict';

	/******************************************
	* Place Modal View
	*/
	var modalUnplanTaskView =  GenericModalView.extend({

		templateHTML: '/templates/modals/tasks/modalUnplanTask.html',

		// The DOM events //
		events: function(){
			return _.defaults({
				'click #btnRemoveTask': 'removeTaskFromSchedule'
			},
				GenericModalView.prototype.events
			);
		},

		/** View Initialization
		*/
		initialize : function(params) {
			this.options = params;

			this.modal = $(this.el);

			// Intervention Model in the Left column //
			this.interModel = this.options.interModel;

			this.render();
		},

		/** Display the view
		*/
		render : function() {
			var self = this;

			// Retrieve the template //
			$.get(app.menus.openstc+this.templateHTML, function(templateData){

				var template = _.template(templateData, {
					lang     : app.lang,
					task     : self.model,
					TaskModel: TaskModel
				});

				self.modal.html(template);
				self.modal.modal('show');
			});

			return this;
		},

		/** Remove the Task from the Calendar
		*/
		removeTaskFromSchedule: function(e){

			var self = this;
			// Set the button in loading State //
			$(e.target).button('loading');

			//Cancel absent task (no intervention)
			if(( TaskModel.status[this.model.toJSON().state].key === TaskModel.status.absent.key ) ){
				this.model.destroy({wait: true})
				.done(function(){
					self.modal.modal('hide');
				})
				.fail(function(){
					console.error(e);
				})
				.always(function(){
					// Reset the button state //
					$(e.target).button('reset');
				});
			}
			//Template task unplanned
			else if(!_.isNull(this.interModel) && !_.isUndefined(this.interModel)  && ( InterventionModel.status[this.interModel.toJSON().state].key === InterventionModel.status.template.key ) ){

				//remove template task
				this.model.destroy({wait: true})
					.done(function(){
						//re-fetch intervention
						$.when(  self.interModel.fetch() )
							.done(function(){
								self.modal.modal('hide');
							});
					})
					.fail(function(){
						console.error(e);
					})
					.always(function(){
						// Reset the button state //
						$(e.target).button('reset');
					});
			}
			//others tasks to unplanned
			else
			{
				//Set Task fields
				var params = {
					state     : TaskModel.status.draft.key,
					user_id   : false,
					team_id   : false,
					partner_id: false,
					date_end  : false,
					date_start: false,
				};
				//Update task and intervention
				this.model.save(params, {patch: true, silent: false})
					.done(function() {
						var ajaxRequests = [self.model.fetch({ data : {fields : self.model.fields} } )];

						if( !_.isUndefined(self.interModel) ){
							//Add ajax request for update intervention
							ajaxRequests.push(self.interModel.fetch());
						}

						$.when( ajaxRequests )
							.done(function(){
								self.modal.modal('hide');
							})
							.fail(function(e){
								console.error(e);
							});
					})
					.always(function(){
						// Reset the button state //
						$(e.target).button('reset');
					});
			}
		}

	});

	return modalUnplanTaskView;

});<|endoftext|>


<commit_before>/**
 * The Timeline date picker view module.
 *
 * @return TorqueTimelineDatePicker view (extends Backbone.View).
 */
define([
  'underscore', 'backbone', 'moment', 'handlebars', 'picker', 'pickadate',
  'map/presenters/TorqueTimelinePresenter',
  'text!templates/datePickerTorque.handlebars',
  'text!templates/datePickerTorque-legend.handlebars'
], function(
  _, Backbone, moment, Handlebars, Picker, Pickadate,
  Presenter,
  tpl, legendTpl) {

  'use strict';

  var SelectedDates = Backbone.Model.extend({
    getRange: function() {
      return [this.get('startDate'), this.get('endDate')];
    },

    setDate: function(id, date) {
      var otherDate;
      if (id === 'startDate') {
        otherDate = this.get('endDate');
        if (date.isAfter(otherDate)) { return; }
      } else if (id === 'endDate') {
        otherDate = this.get('startDate');
        if (date.isBefore(otherDate)) { return; }
      }

      if (date.toDate().getHours() !== 0) {
        var tzOffset = date.toDate().getTimezoneOffset();
        if (tzOffset > 0) { date = date.add(tzOffset, 'minutes'); }
      }
      this.set(id, date);
    }
  });

  var TorqueTimelineDatePicker = Backbone.View.extend({

    className: 'timeline-date-pickers',

    template: Handlebars.compile(tpl),
    legendTemplate: Handlebars.compile(legendTpl),

    initialize: function(options) {
      options = options || {};
      this.presenter = options.presenter;
      this.dataService = options.dataService;
      this.layer = options.layer;
      this.onChange = options.onChange;

      this.selectedDates = new SelectedDates();
      this.selectedDates.setDate('startDate',
        moment.utc(options.dateRange.start));
      this.selectedDates.setDate('endDate',
        moment.utc(options.dateRange.end));
      this.listenTo(this.selectedDates, 'change', this.updateTorque);

      this.retrieveAvailableDates();
    },

    render: function() {
      this.$el.html(this.template({
        title: this.layer.title,
        startDate: this.selectedDates.get('startDate'),
        endDate: this.selectedDates.get('endDate')
      }));
      this.renderPickers();

      return this;
    },

    renderPickers: function() {
      var context = this;
      var onPickerRender = function() {
        var pickerContext = this;

        this.$root.find('.picker__day').each(function() {
          var $el = $(this);

          var date = moment($el.data('pick'));
          var day = date.dayOfYear();

          var histogram = context.histograms[date.year()];
          if (histogram) {
            if (histogram[day - 1] > 0) {

              // Disabled dates to prevent inverted selected dates
              //   e.g. picking 6/09 for start, and 4/09 for end
              var id = pickerContext.component.$node.attr('id');
              var endDate = context.selectedDates.get('endDate');
              var startDate = context.selectedDates.get('startDate');

              if (id === 'startDate') {
                if (!date.isAfter(endDate)) {
                  $el.addClass('picker__has_data');
                }
              } else if (id === 'endDate') {
                if (!date.isBefore(startDate)) {
                  $el.addClass('picker__has_data');
                }
              }
            }
          }
        });

        var $footer = this.$root.find('.picker__footer');
        $footer.prepend(context.legendTemplate());
      };

      var onPickerOpen = function() {
        this.component.disabled = function(dateToVerify) {
          var date = moment.utc(dateToVerify.obj);
          var id = this.component.$node.attr('id');

          if (id === 'startDate') {
            var endDate = context.selectedDates.get('endDate');
            return (date.isAfter(endDate));
          } else if (id === 'endDate') {
            var startDate = context.selectedDates.get('startDate');
            return (date.isBefore(startDate));
          }

          return false;
        }.bind(this);

        this.render();
      };

      var tzOffset = new Date().getTimezoneOffset();
      var minDate = moment.utc(this.layer.mindate).
        add(tzOffset, 'minutes').
        toDate();

      var maxDate = this.maxDate ? this.maxDate.toDate() :
        moment.utc().add(tzOffset, 'minutes').toDate();

      this.$('.timeline-date-picker').pickadate({
        today: 'Jump to Today',
        min: minDate,
        max: maxDate,
        selectYears: 20,
        selectMonths: true,
        format: 'd mmm yyyy',
        onRender: onPickerRender,
        onOpen: onPickerOpen,
        klass: {
          picker: 'picker -top',
        },
        onSet: function(event) {
          if (event.select !== undefined) {
            var id = this.component.$node.attr('id');
            var timezone = new Date().getTimezoneOffset() * 60 * 1000,
                offsetDate = event.select - timezone;
            context.selectedDates.setDate(id, moment.utc(offsetDate));
          }
        }
      });
    },

    updateTorque: function() {
      var dateRange = this.selectedDates.getRange();
      this.onChange(dateRange);
      this.presenter.setTorqueDateRange(dateRange);
    },

    setMinMaxDate: function(data) {
      this.minDate = moment.utc(data.minDate).startOf('day');
      this.maxDate = moment.utc(data.maxDate);

      var minDate = this.minDate.clone().toDate();
      var maxDate = this.maxDate.clone().toDate();

      this.$('#startDate').pickadate('picker').set('min', minDate);
      this.$('#endDate').pickadate('picker').set('max', maxDate);
    },

    retrieveAvailableDates: function() {
      var dateService = new this.dataService();
      this.histograms = [];

      dateService.fetchDates().then(function(response) {
        this.histograms = response.counts;
        this.renderPickers();
        this.setMinMaxDate(response);
      }.bind(this));
    }

  });

  return TorqueTimelineDatePicker;

});
<commit_msg>Fix: Timeline<commit_after>/**
 * The Timeline date picker view module.
 *
 * @return TorqueTimelineDatePicker view (extends Backbone.View).
 */
define([
  'underscore', 'backbone', 'moment', 'handlebars', 'picker', 'pickadate',
  'map/presenters/TorqueTimelinePresenter',
  'text!templates/datePickerTorque.handlebars',
  'text!templates/datePickerTorque-legend.handlebars'
], function(
  _, Backbone, moment, Handlebars, Picker, Pickadate,
  Presenter,
  tpl, legendTpl) {

  'use strict';

  var SelectedDates = Backbone.Model.extend({
    getRange: function() {
      return [this.get('startDate'), this.get('endDate')];
    },

    setDate: function(id, date) {
      var otherDate;
      if (id === 'startDate') {
        otherDate = this.get('endDate');
        if (date.isAfter(otherDate)) { return; }
      } else if (id === 'endDate') {
        otherDate = this.get('startDate');
        if (date.isBefore(otherDate)) { return; }
      }

      if (date.toDate().getHours() !== 0) {
        var tzOffset = date.toDate().getTimezoneOffset();
        if (tzOffset > 0) { date = date.add(tzOffset, 'minutes'); }
      }
      this.set(id, date);
    }
  });

  var TorqueTimelineDatePicker = Backbone.View.extend({

    className: 'timeline-date-pickers',

    template: Handlebars.compile(tpl),
    legendTemplate: Handlebars.compile(legendTpl),

    initialize: function(options) {
      options = options || {};
      this.presenter = options.presenter;
      this.dataService = options.dataService;
      this.layer = options.layer;
      this.onChange = options.onChange;

      this.selectedDates = new SelectedDates();
      this.selectedDates.setDate('startDate',
        moment.utc(options.dateRange.start));
      this.selectedDates.setDate('endDate',
        moment.utc(options.dateRange.end));
      this.listenTo(this.selectedDates, 'change', this.updateTorque);

      this.retrieveAvailableDates();
    },

    render: function() {
      this.$el.html(this.template({
        title: this.layer.title,
        startDate: this.selectedDates.get('startDate'),
        endDate: this.selectedDates.get('endDate')
      }));
      this.renderPickers();

      return this;
    },

    renderPickers: function() {
      var context = this;
      var onPickerRender = function() {
        var pickerContext = this;

        this.$root.find('.picker__day').each(function() {
          var $el = $(this);

          var date = moment($el.data('pick'));
          var day = date.dayOfYear();

          var histogram = context.histograms[date.year()];
          if (histogram) {
            if (histogram[day - 1] > 0) {

              // Disabled dates to prevent inverted selected dates
              //   e.g. picking 6/09 for start, and 4/09 for end
              var id = pickerContext.component.$node.attr('id');
              var endDate = context.selectedDates.get('endDate');
              var startDate = context.selectedDates.get('startDate');

              if (id === 'startDate') {
                if (!date.isAfter(endDate)) {
                  $el.addClass('picker__has_data');
                }
              } else if (id === 'endDate') {
                if (!date.isBefore(startDate)) {
                  $el.addClass('picker__has_data');
                }
              }
            }
          }
        });

        var $footer = this.$root.find('.picker__footer');
        $footer.prepend(context.legendTemplate());
      };

      var onPickerOpen = function() {
        this.component.disabled = function(dateToVerify) {
          var date = moment.utc(dateToVerify.obj);
          var id = this.component.$node.attr('id');

          if (id === 'startDate') {
            var endDate = context.selectedDates.get('endDate');
            return (date.isAfter(endDate));
          } else if (id === 'endDate') {
            var startDate = context.selectedDates.get('startDate');
            return (date.isBefore(startDate));
          }

          return false;
        }.bind(this);

        this.render();
      };

      var tzOffset = new Date().getTimezoneOffset();
      var minDate = moment.utc(this.layer.mindate).
        add(tzOffset, 'minutes').
        toDate();

      var maxDate = this.maxDate ? this.maxDate.toDate() :
        moment.utc().add(tzOffset, 'minutes').toDate();

      this.$('.timeline-date-picker').pickadate({
        today: 'Jump to Today',
        min: minDate,
        max: maxDate,
        selectYears: 20,
        selectMonths: true,
        format: 'd mmm yyyy',
        onRender: onPickerRender,
        onOpen: onPickerOpen,
        klass: {
          picker: 'picker -top',
        },
        onSet: function(event) {
          if (event.select !== undefined) {
            var id = this.component.$node.attr('id');
            var timezone = new Date().getTimezoneOffset() * 60 * 1000,
                offsetDate = event.select - timezone;
            context.selectedDates.setDate(id, moment.utc(offsetDate));
          }
        }
      });
    },

    updateTorque: function() {
      var dateRange = this.selectedDates.getRange();
      this.onChange(dateRange);
      this.presenter.setTorqueDateRange(dateRange);
    },

    setMinMaxDate: function(data) {
      var tzOffset = new Date().getTimezoneOffset() + 60;
      this.minDate = moment.utc(data.minDate).endOf('day');
      this.maxDate = moment.utc(data.maxDate);

      var minDate = this.minDate.clone().add(tzOffset, 'minutes').toDate();
      var maxDate = this.maxDate.clone().add(tzOffset, 'minutes').toDate();
      this.$('#startDate').pickadate('picker').set('min', minDate);
      this.$('#endDate').pickadate('picker').set('max', maxDate);
    },

    retrieveAvailableDates: function() {
      var dateService = new this.dataService();
      this.histograms = [];

      dateService.fetchDates().then(function(response) {
        this.histograms = response.counts;
        this.renderPickers();
        this.setMinMaxDate(response);
      }.bind(this));
    }

  });

  return TorqueTimelineDatePicker;

});
<|endoftext|>


<commit_before>/*!
 *   Angular Smooth Scroll (ngSmoothScroll)
 *   Animates scrolling to elements, by David Oliveros.
 *
 *   Callback hooks contributed by Ben Armston https://github.com/benarmston
 *   Easing support contributed by Willem Liu. https://github.com/willemliu
 *   Easing functions forked from Gaëtan Renaudeau. https://gist.github.com/gre/1650294
 *   Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman. https://github.com/alexguzman
 *   Support for scrolling in custom containers by Joseph Matthias Goh. https://github.com/zephinzer
 *   Influenced by Chris Ferdinandi
 *   https://github.com/cferdinandi
 *
 *   Version: 2.0.0
 *   License: MIT
 */

(function () {
  'use strict';

  var module = angular.module('smoothScroll', []);

  /**
   * Smooth scrolls the window/div to the provided element.
   *
   * 20150713 EDIT - zephinzer
   *  Added new option - containerId to account for scrolling within a DIV
   */
  var smoothScroll = function (element, options) {
    options = options || {};

    // Options
    var duration = options.duration || 800,
      offset = options.offset || 60,
      easing = options.easing || 'easeInOutQuart',
      callbackBefore = options.callbackBefore || function() {},
      callbackAfter = options.callbackAfter || function() {},
      container = document.getElementById(options.containerId) || null,
      containerPresent = (container != undefined && container != null);

    /**
     * Retrieve current location
     */
    var getScrollLocation = function() {
      if(containerPresent) {
        return container.scrollTop;
      } else {
        if(window.pageYOffset) {
          return window.pageYOffset;
        } else {
          return document.documentElement.scrollTop;
        }
      }
    };

    /**
     * Calculate easing pattern.
     *
     * 20150713 edit - zephinzer
     * - changed if-else to switch
     * @see http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
     */
    var getEasingPattern = function(type, time) {
      switch(type) {
        case 'easeInQuad':    return time * time; // accelerating from zero velocity
        case 'easeOutQuad':   return time * (2 - time); // decelerating to zero velocity
        case 'easeInOutQuad':   return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
        case 'easeInCubic':   return time * time * time; // accelerating from zero velocity
        case 'easeOutCubic':  return (--time) * time * time + 1; // decelerating to zero velocity
        case 'easeInOutCubic':  return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
        case 'easeInQuart':   return time * time * time * time; // accelerating from zero velocity
        case 'easeOutQuart':  return 1 - (--time) * time * time * time; // decelerating to zero velocity
        case 'easeInOutQuart':  return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
        case 'easeInQuint':   return time * time * time * time * time; // accelerating from zero velocity
        case 'easeOutQuint':  return 1 + (--time) * time * time * time * time; // decelerating to zero velocity
        case 'easeInOutQuint':  return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
        default:        return time;
      }
    };

    /**
     * Calculate how far to scroll
     */
    var getEndLocation = function(element) {
      var location = 0;
      if (element.offsetParent) {
        do {
          location += element.offsetTop;
          element = element.offsetParent;
        } while (element);
      }
      location = Math.max(location - offset, 0);
      return location;
    };

    // Initialize the whole thing
    setTimeout( function() {
      var currentLocation = null,
        startLocation   = getScrollLocation(),
        endLocation   = getEndLocation(element),
        timeLapsed    = 0,
        distance    = endLocation - startLocation,
        percentage,
        position,
        scrollHeight,
        internalHeight;

      /**
       * Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page)
       */
      var stopAnimation = function () {
        currentLocation = getScrollLocation();
        if(containerPresent) {
          scrollHeight = container.scrollHeight;
          internalHeight = container.clientHeight + currentLocation;
        } else {
          scrollHeight = document.body.scrollheight;
          internalHeight = window.innerHeight + currentLocation;
        }

        if (
          ( // condition 1
            position == endLocation
          ) ||
          ( // condition 2
            currentLocation == endLocation
          ) ||
          ( // condition 3
            internalHeight >= scrollHeight
          )
        ) { // stop
          clearInterval(runAnimation);
          callbackAfter(element);
        }
      };

      /**
       * Scroll the page by an increment, and check if it's time to stop
       */
      var animateScroll = function () {
        timeLapsed += 16;
        percentage = ( timeLapsed / duration );
        percentage = ( percentage > 1 ) ? 1 : percentage;
        position = startLocation + ( distance * getEasingPattern(easing, percentage) );
        if(containerPresent) {
          container.scrollTop = position;
        } else {
          window.scrollTo( 0, position );
        }
        stopAnimation();
      };

      callbackBefore(element);
      var runAnimation = setInterval(animateScroll, 16);
    }, 0);
  };

  // Expose the library in a factory
  //
  module.factory('smoothScroll', function() {
    return smoothScroll;
  });

  /**
   * Scrolls the window to this element, optionally validating an expression
   *
   * 20150713 EDIT - zephinzer
   *  Added containerId to attributes for smooth scrolling within a DIV
   */
  module.directive('smoothScroll', ['smoothScroll', function(smoothScroll) {
    return {
      restrict: 'A',
      scope: {
        callbackBefore: '&',
        callbackAfter: '&',
      },
      link: function($scope, $elem, $attrs) {
        if ( typeof $attrs.scrollIf === 'undefined' || $attrs.scrollIf === 'true' ) {
          setTimeout( function() {

            var callbackBefore = function(element) {
              if ( $attrs.callbackBefore ) {
                var exprHandler = $scope.callbackBefore({ element: element });
                if (typeof exprHandler === 'function') {
                  exprHandler(element);
                }
              }
            };

            var callbackAfter = function(element) {
              if ( $attrs.callbackAfter ) {
                var exprHandler = $scope.callbackAfter({ element: element });
                if (typeof exprHandler === 'function') {
                  exprHandler(element);
                }
              }
            };

            smoothScroll($elem[0], {
              duration: $attrs.duration,
              offset: $attrs.offset,
              easing: $attrs.easing,
              callbackBefore: callbackBefore,
              callbackAfter: callbackAfter,
              containerId: $attrs.containerId
            });
          }, 0);
        }
      }
    };
  }]);

  /**
   * Scrolls to a specified element ID when this element is clicked
   *
   * 20150713 EDIT - zephinzer
   *  Added containerId to attributes for smooth scrolling within a DIV
   */
  module.directive('scrollTo', ['smoothScroll', function(smoothScroll) {
    return {
      restrict: 'A',
      scope: {
        callbackBefore: '&',
        callbackAfter: '&',
      },
      link: function($scope, $elem, $attrs) {
        var targetElement;

        $elem.on('click', function(e) {
          targetElement = document.getElementById($attrs.scrollTo);
          if ( !targetElement ) return;

          e.preventDefault();
          e.blur();

          var callbackBefore = function(element) {
            if ( $attrs.callbackBefore ) {
              var exprHandler = $scope.callbackBefore({element: element});
              if (typeof exprHandler === 'function') {
                exprHandler(element);
              }
            }
          };

          var callbackAfter = function(element) {
            if ( $attrs.callbackAfter ) {
              var exprHandler = $scope.callbackAfter({element: element});
              if (typeof exprHandler === 'function') {
                exprHandler(element);
              }
            }
          };

          smoothScroll(targetElement, {
            duration: $attrs.duration,
            offset: $attrs.offset,
            easing: $attrs.easing,
            callbackBefore: callbackBefore,
            callbackAfter: callbackAfter,
            containerId: $attrs.containerId
          });

          return false;
        });
      }
    };
  }]);

}());
<commit_msg>let's try that again<commit_after>/*!
 *   Angular Smooth Scroll (ngSmoothScroll)
 *   Animates scrolling to elements, by David Oliveros.
 *
 *   Callback hooks contributed by Ben Armston https://github.com/benarmston
 *   Easing support contributed by Willem Liu. https://github.com/willemliu
 *   Easing functions forked from Gaëtan Renaudeau. https://gist.github.com/gre/1650294
 *   Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman. https://github.com/alexguzman
 *   Support for scrolling in custom containers by Joseph Matthias Goh. https://github.com/zephinzer
 *   Influenced by Chris Ferdinandi
 *   https://github.com/cferdinandi
 *
 *   Version: 2.0.0
 *   License: MIT
 */

(function () {
  'use strict';

  var module = angular.module('smoothScroll', []);

  /**
   * Smooth scrolls the window/div to the provided element.
   *
   * 20150713 EDIT - zephinzer
   *  Added new option - containerId to account for scrolling within a DIV
   */
  var smoothScroll = function (element, options) {
    options = options || {};

    // Options
    var duration = options.duration || 800,
      offset = options.offset || 60,
      easing = options.easing || 'easeInOutQuart',
      callbackBefore = options.callbackBefore || function() {},
      callbackAfter = options.callbackAfter || function() {},
      container = document.getElementById(options.containerId) || null,
      containerPresent = (container != undefined && container != null);

    /**
     * Retrieve current location
     */
    var getScrollLocation = function() {
      if(containerPresent) {
        return container.scrollTop;
      } else {
        if(window.pageYOffset) {
          return window.pageYOffset;
        } else {
          return document.documentElement.scrollTop;
        }
      }
    };

    /**
     * Calculate easing pattern.
     *
     * 20150713 edit - zephinzer
     * - changed if-else to switch
     * @see http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
     */
    var getEasingPattern = function(type, time) {
      switch(type) {
        case 'easeInQuad':    return time * time; // accelerating from zero velocity
        case 'easeOutQuad':   return time * (2 - time); // decelerating to zero velocity
        case 'easeInOutQuad':   return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
        case 'easeInCubic':   return time * time * time; // accelerating from zero velocity
        case 'easeOutCubic':  return (--time) * time * time + 1; // decelerating to zero velocity
        case 'easeInOutCubic':  return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
        case 'easeInQuart':   return time * time * time * time; // accelerating from zero velocity
        case 'easeOutQuart':  return 1 - (--time) * time * time * time; // decelerating to zero velocity
        case 'easeInOutQuart':  return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
        case 'easeInQuint':   return time * time * time * time * time; // accelerating from zero velocity
        case 'easeOutQuint':  return 1 + (--time) * time * time * time * time; // decelerating to zero velocity
        case 'easeInOutQuint':  return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
        default:        return time;
      }
    };

    /**
     * Calculate how far to scroll
     */
    var getEndLocation = function(element) {
      var location = 0;
      if (element.offsetParent) {
        do {
          location += element.offsetTop;
          element = element.offsetParent;
        } while (element);
      }
      location = Math.max(location - offset, 0);
      return location;
    };

    // Initialize the whole thing
    setTimeout( function() {
      var currentLocation = null,
        startLocation   = getScrollLocation(),
        endLocation   = getEndLocation(element),
        timeLapsed    = 0,
        distance    = endLocation - startLocation,
        percentage,
        position,
        scrollHeight,
        internalHeight;

      /**
       * Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page)
       */
      var stopAnimation = function () {
        currentLocation = getScrollLocation();
        if(containerPresent) {
          scrollHeight = container.scrollHeight;
          internalHeight = container.clientHeight + currentLocation;
        } else {
          scrollHeight = document.body.scrollheight;
          internalHeight = window.innerHeight + currentLocation;
        }

        if (
          ( // condition 1
            position == endLocation
          ) ||
          ( // condition 2
            currentLocation == endLocation
          ) ||
          ( // condition 3
            internalHeight >= scrollHeight
          )
        ) { // stop
          clearInterval(runAnimation);
          callbackAfter(element);
        }
      };

      /**
       * Scroll the page by an increment, and check if it's time to stop
       */
      var animateScroll = function () {
        timeLapsed += 16;
        percentage = ( timeLapsed / duration );
        percentage = ( percentage > 1 ) ? 1 : percentage;
        position = startLocation + ( distance * getEasingPattern(easing, percentage) );
        if(containerPresent) {
          container.scrollTop = position;
        } else {
          window.scrollTo( 0, position );
        }
        stopAnimation();
      };

      callbackBefore(element);
      var runAnimation = setInterval(animateScroll, 16);
    }, 0);
  };

  // Expose the library in a factory
  //
  module.factory('smoothScroll', function() {
    return smoothScroll;
  });

  /**
   * Scrolls the window to this element, optionally validating an expression
   *
   * 20150713 EDIT - zephinzer
   *  Added containerId to attributes for smooth scrolling within a DIV
   */
  module.directive('smoothScroll', ['smoothScroll', function(smoothScroll) {
    return {
      restrict: 'A',
      scope: {
        callbackBefore: '&',
        callbackAfter: '&',
      },
      link: function($scope, $elem, $attrs) {
        if ( typeof $attrs.scrollIf === 'undefined' || $attrs.scrollIf === 'true' ) {
          setTimeout( function() {

            var callbackBefore = function(element) {
              if ( $attrs.callbackBefore ) {
                var exprHandler = $scope.callbackBefore({ element: element });
                if (typeof exprHandler === 'function') {
                  exprHandler(element);
                }
              }
            };

            var callbackAfter = function(element) {
              if ( $attrs.callbackAfter ) {
                var exprHandler = $scope.callbackAfter({ element: element });
                if (typeof exprHandler === 'function') {
                  exprHandler(element);
                }
              }
            };

            smoothScroll($elem[0], {
              duration: $attrs.duration,
              offset: $attrs.offset,
              easing: $attrs.easing,
              callbackBefore: callbackBefore,
              callbackAfter: callbackAfter,
              containerId: $attrs.containerId
            });
          }, 0);
        }
      }
    };
  }]);

  /**
   * Scrolls to a specified element ID when this element is clicked
   *
   * 20150713 EDIT - zephinzer
   *  Added containerId to attributes for smooth scrolling within a DIV
   */
  module.directive('scrollTo', ['smoothScroll', function(smoothScroll) {
    return {
      restrict: 'A',
      scope: {
        callbackBefore: '&',
        callbackAfter: '&',
      },
      link: function($scope, $elem, $attrs) {
        var targetElement;

        $elem.on('click', function(e) {
          targetElement = document.getElementById($attrs.scrollTo);
          if ( !targetElement ) return;

          e.preventDefault();
          targetElement.blur();

          var callbackBefore = function(element) {
            if ( $attrs.callbackBefore ) {
              var exprHandler = $scope.callbackBefore({element: element});
              if (typeof exprHandler === 'function') {
                exprHandler(element);
              }
            }
          };

          var callbackAfter = function(element) {
            if ( $attrs.callbackAfter ) {
              var exprHandler = $scope.callbackAfter({element: element});
              if (typeof exprHandler === 'function') {
                exprHandler(element);
              }
            }
          };

          smoothScroll(targetElement, {
            duration: $attrs.duration,
            offset: $attrs.offset,
            easing: $attrs.easing,
            callbackBefore: callbackBefore,
            callbackAfter: callbackAfter,
            containerId: $attrs.containerId
          });

          return false;
        });
      }
    };
  }]);

}());
<|endoftext|>


<commit_before>app.controller('DemoCtrl',DemoCtrl);function DemoCtrl($timeout,$q,$log,$scope,$http,$window,$location,$state){$scope.showTopGitList=false
$scope.gitList=false
var self=this;self.states=loadAll();self.querySearch=querySearch;self.selectedItemChange=selectedItemChange;self.newState=newState;function newState(state){alert("Sorry! You'll need to create a Constitution for "+state+" first!");}function querySearch(query){var results=query?self.states.filter(createFilterFor(query)):self.states;return results;}function selectedItemChange(item){$scope.item=item
$state.go('language.lang',{item_name:item})}function loadAll(){$scope.language_list=["1C Enterprise","ABAP","ActionScript","Ada","Agda","AGS Script","Alloy","AMPL","ANTLR","ApacheConf","Apex","API Blueprint","APL","AppleScript","Arc","Arduino","ASP","AspectJ","Assembly","ATS","Augeas","AutoHotkey","AutoIt","Awk","Batchfile","Befunge","Bison","BitBake","BlitzBasic","BlitzMax","Bluespec","Boo","Brainfuck","Brightscript","Bro","C","C#","C++","Cap'n Proto","CartoCSS","Ceylon","Chapel","Charity","ChucK","Cirru","Clarion","Clean","Click","CLIPS","Clojure","CMake","COBOL","CoffeeScript","ColdFusion","Common Lisp","Component Pascal","Cool","Coq","Crystal","Csound","Csound Document","Csound Score","CSS","Cucumber","Cuda","Cycript","D","Darcs Patch","Dart","Diff","DIGITAL Command Language","DM","Dogescript","DTrace","Dylan","E","Eagle","eC","ECL","Eiffel","Elixir","Elm","Emacs Lisp","EmberScript","EQ","Erlang","F#","Factor","Fancy","Fantom","FLUX","Forth","FORTRAN","FreeMarker","Frege","Game Maker Language","GAMS","GAP","GCC Machine Description","GDB","GDScript","Genshi","Gettext Catalog","GLSL","Glyph","Gnuplot","Go","Golo","Gosu","Grace","Grammatical Framework","Groff","Groovy","Hack","Handlebars","Harbour","Haskell","Haxe","HCL","HLSL","HTML","Hy","HyPhy","IDL","Idris","IGOR Pro","Inform 7","Inno Setup","Io","Ioke","Isabelle","J","Jasmin","Java","JavaScript","JFlex","JSONiq","Julia","Jupyter Notebook","KiCad","Kit","Kotlin","KRL","LabVIEW","Lasso","Lean","Lex","LilyPond","Limbo","Liquid","LiveScript","LLVM","Logos","Logtalk","LOLCODE","LookML","LoomScript","LSL","Lua","M","M4","Makefile","Mako","Markdown","Mask","Mathematica","Matlab","Max","MAXScript","Mercury","Metal","MiniD","Mirah","Modelica","Modula-2","Module Management System","Monkey","Moocode","MoonScript","MTML","mupad","Myghty","NCL","Nemerle","nesC","NetLinx","NetLinx+ERB","NetLogo","NewLisp","Nginx","Nimrod","Nit","Nix","NSIS","Nu","Objective-C","Objective-C++","Objective-J","OCaml","Omgrofl","ooc","Opa","Opal","OpenEdge ABL","OpenSCAD","Ox","Oxygene","Oz","Pan","Papyrus","Parrot","Pascal","PAWN","Perl","Perl6","PHP","PicoLisp","PigLatin","Pike","PLpgSQL","PLSQL","PogoScript","Pony","PostScript","POV-Ray SDL","PowerBuilder","PowerShell","Processing","Prolog","Propeller Spin","Protocol Buffer","Puppet","Pure Data","PureBasic","PureScript","Python","QMake","QML","R","Racket","Ragel in Ruby Host","RAML","RDoc","REALbasic","Rebol","Red","Redcode","Ren'Py","RenderScript","REXX","RobotFramework","Rouge","Ruby","RUNOFF","Rust","SaltStack","SAS","Scala","Scheme","Scilab","Self","Shell","ShellSession","Shen","Slash","Smali","Smalltalk","Smarty","SMT","SourcePawn","SQF","SQL","SQLPL","Squirrel","SRecode Template","Stan","Standard ML","Stata","SuperCollider","Swift","SystemVerilog","Tcl","Tea","Terra","TeX","Thrift","TLA","Turing","TXL","TypeScript","Uno","UnrealScript","UrWeb","Vala","VCL","Verilog","VHDL","VimL","Visual Basic","Volt","Vue","Web Ontology Language","WebIDL","wisp","X10","xBase","XC","XML","Xojo","XPages","XProc","XQuery","XS","XSLT","Xtend","Yacc","Zephir","Zimpl"]
return $scope.language_list}function createFilterFor(query){var lowercaseQuery=angular.lowercase(query);return function filterFn(state){return(state.toLowerCase().indexOf(lowercaseQuery)===0);};}}<commit_msg>author controller updated<commit_after>app.controller("authorPageController",["$scope","$http","$stateParams","$window","$rootScope","$state",function(a,r,g,o,b,t){a.author_show=!1,a.loader=!0,a.showLoadMore=!1;var e=["rgba(236,100,75,0.5)","rgba(210, 77, 87,0.5)","rgba(242, 38, 19,0.5)","rgba(150, 40, 27,0.5)","rgba(239, 72, 54,0.5)","rgba(214, 69, 65,0.5)","rgba(192, 57, 43,0.5)","rgba(207, 0, 15,0.5)","rgba(231, 76, 60,0.5)","rgba(219, 10, 91,0.5)","rgba(246, 71, 71,0.5)","rgba(241, 169, 160,0.5)","rgba(210, 82, 127,0.5)","rgba(224, 130, 131,0.5)","rgba(246, 36, 89,0.5)","rgba(226, 106, 106,0.5)","rgba(220, 198, 224,0.5)","rgba(102, 51, 153,0.5)","rgba(103, 65, 114,0.5)","rgba(174, 168, 211,0.5)","rgba(145, 61, 136,0.5)","rgba(154, 18, 179,0.5)","rgba(191, 85, 236,0.5)","rgba(190, 144, 212,0.5)","rgba(142, 68, 173,0.5)","rgba(155, 89, 182,0.5)","rgba(68,108,179,0.5)","rgba(228, 241, 254,0.5)","rgba(65, 131, 215,0.5)","rgba(89, 171, 227,0.5)","rgba(129, 207, 224,0.5)","rgba(82, 179, 217,0.5)","rgba(197, 239, 247,0.5)","rgba(34, 167, 240,0.5)","rgba(52, 152, 219,0.5)","rgba(44, 62, 80,0.5)","rgba(25, 181, 254,0.5)","rgba(25, 181, 254,0.5)","rgba(34, 49, 63,0.5)","rgba(107, 185, 240,0.5)","rgba(30, 139, 195,0.5)","rgba(58, 83, 155,0.5)","rgba(52, 73, 94,0.5)","rgba(103, 128, 159,0.5)","rgba(37, 116, 169,0.5)","rgba(78,205,196,0.5)","rgba(162, 222, 208,0.5)","rgba(135, 211, 124,0.5)","rgba(38, 166, 91,0.5)","rgba(3, 201, 169,0.5)","rgba(104, 195, 163,0.5)","rgba(101, 198, 187,0.5)","rgba(27, 188, 155,0.5)","rgba(27, 163, 156,0.5)","rgba(102, 204, 153,0.5)","rgba(54, 215, 183,0.5)","rgba(200, 247, 197,0.5)","rgba(134, 226, 213,0.5)","rgba(235, 149, 50,0.5)"],s=[];a.autor=g.author,a.page_no=1,b.title=a.autor+" Page";var n="https://api.github.com/users/"+a.autor,p="https://api.github.com/users/"+a.autor+"/repos?sort=updated&page="+a.page_no+"&per_page=100";r.get(n).success(function(g){a.author_data=g,r.get(p).success(function(r){for(var g=0;g<r.length;g++)s.push(r[g]),s[g].color=e[Math.floor(Math.random()*e.length)];a.repo_data=s})})["finally"](function(){a.author_show=!0,a.loader=!1}),a.loadMore=function(){a.showLoadMore=!0,a.page_no=a.page_no+1;var g="https://api.github.com/users/"+a.autor+"/repos?sort=updated&page="+a.page_no+"&per_page=100";r.get(g).success(function(r){for(var g=0;g<r.length;g++)s.push(r[g]),s[g].color=e[Math.floor(Math.random()*e.length)],console.log(s[g].color);a.repo_data=s})["finally"](function(){a.showLoadMore=!1})}}]);<|endoftext|>


<commit_before>'use strict';

var assert = require("assert");
var exec = require('child_process').exec;
var path = require('path');

describe('<%= slugname %> bin', function(){
	var cmd = 'node '+path.join(__dirname, '../bin/<%= slugname %>')+' ';
	console.log(cmd);

	it('--help should run without errors', function(done) {
		exec(cmd+'--help', function (error, stdout, stderr) {
			assert(!error);
			assert(stdout.match(/Usage: <%= slugname %>/i));
			done();
		});
	});

	it('--version should run without errors', function(done) {
		exec(cmd+'--version', function (error, stdout, stderr) {
			assert(!error);
			assert(stdout.match('<%= version %>'));
			done();
		});
	});

	it('should return error on missing command', function(done) {
        this.timeout(4000);

		exec(cmd, function (error, stdout, stderr) {
			assert(error);
			assert.equal(error.code,1);
			assert(stdout.match(/No command specified/i));
			done();
		});

	});

	it('should return error on unknown command', function(done) {
        this.timeout(4000);

		exec(cmd+'junkcmd', function (error, stdout, stderr) {
			assert(error);
			assert.equal(error.code,1);
			assert(stdout.match(/is not a known command/i));
			done();
		});
	});

});
<commit_msg>Fixed errors in template's test<commit_after>'use strict';

var assert = require("assert");
var exec = require('child_process').exec;
var path = require('path');

describe('<%= slugname %> bin', function(){
	var cmd = 'node '+path.join(__dirname, '../bin/<%= slugname %>')+' ';
	console.log(cmd);

	it('--help should run without errors', function(done) {
		exec(cmd+'--help', function (error, stdout, stderr) {
			assert(!error);
			done();
		});
	});

	it('--version should run without errors', function(done) {
		exec(cmd+'--version', function (error, stdout, stderr) {
			assert(!error);
			done();
		});
	});

	it('should return error on missing command', function(done) {
        this.timeout(4000);

		exec(cmd, function (error, stdout, stderr) {
			assert(error);
			assert.equal(error.code,1);
			done();
		});

	});

	it('should return error on unknown command', function(done) {
        this.timeout(4000);

		exec(cmd+'junkcmd', function (error, stdout, stderr) {
			assert(error);
			assert.equal(error.code,1);
			done();
		});
	});

});
<|endoftext|>