<commit_before>/* global $, telescopicIntro, recentArticles, authorLinks, siteStrategies, SimpleSearch, pageId, siteAuthors */

var shuffleStrategies = function () {
  var li = $('.strategies-list > ul > li')
  li.detach().sort(function (a, b) {
    return parseInt(Math.random() * 10) % 2
  })
  $('.strategies-list > ul').empty().append(li)
}

var initTelescopicText = function (telescopicTextJSON, telescopicTextContainer) {
  var compose = function (textObject) {
    var story = []
    var keys = Object.keys(textObject)

    for (var i in keys) {
      var key1 = keys[i]
      var key2 = ''
      var lastIndex = key1.length - 1
      if (key1.indexOf(',') === lastIndex) {
        key1 = key1.substring(0, lastIndex)
        key2 = ','
      }
      var val = textObject[keys[i]]
      if (val === null || typeof val === 'undefined') {
        story.push('' + keys[i] + '')
      } else if (typeof val === 'string') {
        story.push('' + key1 + '' + key2)
      } else if (typeof val === 'object') {
        var chapter = compose(val)
        var id = i + '-' + keys[i].toLowerCase().replace(/([^A-Z0-9])+/ig, '')
        story.push('' + key1 + '' + key2 + '')
        story.push('' + chapter + '')
      }
    }

    return story.join(' ')
  }

  var textobj = typeof telescopicTextJSON === 'string' ? JSON.parse(telescopicTextJSON) : telescopicTextJSON
  var telescopicTextContent = compose(textobj)

  $(telescopicTextContainer).html(telescopicTextContent)
  var reset = $('')
  reset.on('click', function () {
    $('.telescopic-content:not(.hidden)').addClass('hidden')
    $('.telescopic-root.hidden').removeClass('hidden')
  })
  $(telescopicTextContainer).append(reset)

  $('span[data-controls].telescopic-link')
    .each(function () {
      $(this).on('click', function (event) {
        $(this).parent().addClass('hidden')
        $('#' + $(this).data('controls')).removeClass('hidden')
      })
    })
}

var getIconClass = function (service) {
  if (service) {
    var s = service.toLowerCase().replace(/\s/g, '')
    if (s.indexOf('github') > -1) {
      return 'fa fa-github'
    } else if (s.indexOf('twitter') > -1) {
      return 'fa fa-twitter'
    } else if (s.indexOf('stackoverflow') > -1) {
      return 'fa fa-stack-overflow'
    } else if (s.indexOf('medium') > -1) {
      return 'fa fa-medium'
    } else if (s.indexOf('linkedin') > -1) {
      return 'fa fa-linkedin'
    } else if (s.indexOf('http') === -1 && s.indexOf('@') > -1) {
      return 'fa fa-envelope'
    } else if (s.indexOf('http') > -1) {
      return 'fa fa-laptop'
    }
  }
}

var initAuthorLinks = function (authorLinks, authorLinksContainer) {
  var links = []
  var icon = null
  var mail = false
  var authorlinks = typeof authorLinks === 'string' ? JSON.parse(authorLinks) : authorLinks
  if (!authorlinks.length) {
    var keys = Object.keys(authorLinks)
    for (var i = 0; i < keys.length; i++) {
      icon = getIconClass(keys[i])
      mail = keys[i] && keys[i].indexOf('http') === -1 && keys[i].indexOf('@') > -1
      if (icon) {
        if (mail) {
          links.push('')
        } else {
          links.push('')
        }
      }
    }
  } else {
    for (var j = 0; j < authorLinks.length; j++) {
      icon = getIconClass(authorLinks[j])
      mail = authorLinks[j] && authorLinks[j].indexOf('http') === -1 && authorLinks[j].indexOf('@') > -1
      if (icon) {
        if (mail) {
          links.push('')
        } else {
          links.push('')
        }
      }
    }
  }

  $(authorLinksContainer).html(links)
}

var randomizeRecent = function (newArticles, recentContainer) {
  var hero = newArticles[Math.floor(Math.random() * 5)]
  var mainLink
  var pubName
  var icon

  if (hero.links['Compose Articles']) {
    mainLink = hero.links['Compose Articles']
    pubName = 'Compose.com/articles'
    icon = 'cubes'
  } else if (hero.links['Medium']) {
    mainLink = hero.links['Medium']
    pubName = 'Medium.com'
    icon = 'medium'
  } else if (hero.links['GitHub']) {
    mainLink = hero.links['GitHub']
    pubName = 'GitHub'
    icon = 'github'
  } else if (hero.links['Notebook']) {
    mainLink = hero.links['Notebook']
    pubName = 'Data Science Notebook'
    icon = 'book'
  } else if (hero.links['npm']) {
    mainLink = hero.links['npm']
    pubName = 'npmjs.com'
    icon = 'code'
  } else if (hero.links['dataset']) {
    mainLink = hero.links['dataset']
    pubName = 'Dataset'
    icon = 'database'
  } else {
    mainLink = hero.links[ Object.keys(hero.links)[0] ]
    pubName = 'External Site'
    icon = 'newspaper-o'
  }

  var html = `${hero.title}

${hero.headline}

    ${pubName}`

  $(recentContainer).append(html)
}

window.devadvo = {
  onBefore: function (q, options, paging, searchurl) {
    $('.projects-next, .projects-prev').prop('disabled', true)
  },

  searchOnData: function (results) {
    if (results.data.rows) {
      // grab first strategy reference
      results.data.rows = results.data.rows.map(function (row) {
        if (row.strategies && row.strategies.length && row.strategies[0] !== '__') {
          row.firstStrategy = {
            'name': siteStrategies[row.strategies[0]] || row.strategies[0],
            'link': row.strategies[0]
          }
        }
        return row
      })
    }
    return results
  },

  searchOnSuccess: function (results) {
    if (results.data && results.data.rows && results.data.rows.length > 0) {
      var resultslist = $('.projects-search-results ul').empty()
      results.data.rows.forEach(function (project) {
        var tags = project.tags && project.tags.length ? ('
' + project.tags.join('

') + '
') : ''
        var linkkeys = Object.keys(project.links)
        var links = ''
        for (var i = 0; i < linkkeys.length; i++) {
          if (i > 0) {
            links += ' | '
          }
          links += `<a href="${project.links[linkkeys[i]]}" class="strategy-project-link" title="'${project.title} on ${linkkeys[i]}' ">${linkkeys[i]}</a>`
          if (i > 0) {
            links += ''
          }
        }

        var strategy = ''
        if (project.firstStrategy) {
          strategy = `<a href="${project.firstStrategy.link}" class="unheader" title="More on our '${project.firstStrategy.name}' strategy">${project.firstStrategy.name}</a>`
        }

        var projdate = ''
        // if (project.date) {
        //   projdate = strategy ? (' – ' + project.date) : project.date
        // }

        var temp = `

              ${strategy}${projdate}

              ${project.title}

              ${links}

 ${project.headline}

${tags}

`

        resultslist.append(temp)

        $('.strategy-project-tags li:not(.bound)')
          .addClass('bound')
          .attr('role', 'button')
          .on('click', function () {
            var t = $(this).text()
            t = 'tags:' + (t.indexOf(' ') === -1 ? t : ('"' + t + '"'))
            var q = window.simplesearchUtil.queryString(t)
            window.simplesearchUtil.search(q)
          })
      })

      if (results.paging) {
        $('.projects-prev').attr('disabled', !results.paging.bookmarks || results.paging.bookmarks.length <= 1)
        $('.projects-next').attr('disabled', results.paging.hasMore !== true)

        var total = results.data.total_rows
        var page = results.paging.bookmarks.length - 1
        var limit = results.paging.limit
        var start = (page * limit) + 1
        var end = start + limit - 1
        if (start < 1) { start = 1 }
        if (total < 1) { start = 0 }
        if (end > total) { end = total }
        if (start > end) { end = start }
        var currentpage = page + 1
        var lastpage = Math.floor(total / limit) + (total % limit > 0 ? 1 : 0)
        console.log(start, end, total, currentpage, lastpage)
        $('.projects-current').text(currentpage)
        $('.projects-last').text(lastpage)
      }
    }

    if (results.facets) {
      $('[data-search-facetkey="tags"]').parent().prependTo('.simplesearch-facets-list')
      $('.simplesearch-facet-key:not(.bound)')
        .addClass('bound')
        .on('click', function () {
          $(this)
            .toggleClass('expanded')
            .next('.simplesearch-facet-value-list')
            .slideToggle('slow')

          var facet = $(this).attr('data-search-facetkey')
          if ($(this).hasClass('expanded') && window.devadvo.expandedfacets.indexOf(facet) === -1) {
            window.devadvo.expandedfacets.push(facet)
          } else if (window.devadvo.expandedfacets.indexOf(facet) > -1) {
            window.devadvo.expandedfacets.splice(window.devadvo.expandedfacets.indexOf(facet), 1)
          }
        })

      if (results.paging && results.paging.query && results.paging.query !== '*:*') {
        var query = results.paging.query.split(' AND ')
        $('.simplesearch-facet-value-name').each(function () {
          var q = $(this).attr('data-search-query')
          if (query.indexOf(q) >= 0) {
            $(this)
              .addClass('filtered')
              .closest('.simplesearch-facet-value-list')
              .css('display', 'block')
              .prev('.simplesearch-facet-key')
              .addClass('expanded')
          }
        })
      }

      if (window.devadvo.expandedfacets.length) {
        window.devadvo.expandedfacets.forEach(function (facet) {
          $(`[data-search-facetkey="${facet}"]:not(.expanded)`)
            .toggleClass('expanded')
            .next('.simplesearch-facet-value-list')
            .css('display', 'block')
        })
      }

      $('[data-search-facetkey="lead"]').text('Authors')
      $('[data-search-query^="lead:"]').each(function () {
        $(this).text(siteAuthors[$(this).text()])
      })
    }
  }
}

var initSearch = function () {
  window.devadvo.expandedfacets = ['tags']
  window.simplesearchUtil = new SimpleSearch('https://advo-projects.mybluemix.net', {
    onSuccess: window.devadvo.searchOnSuccess,
    onData: window.devadvo.searchOnData,
    onBefore: window.devadvo.onBefore
  }, {
    inputField: '#projects-search-input',
    searchButton: false,
    facetsList: '.projects-search-facets'
  }, {
    deepLinking: true,
    params: 'sort="-date<string>"'
  })

  $('.projects-prev').on('click', function () {
    $('html, body').animate({ scrollTop: 0 })
    $('.projects-search-results ul')
      .empty()
      .html('
⌒
')
    window.simplesearchUtil.prev()
  })
  $('.projects-next').on('click', function () {
    $('html, body').animate({ scrollTop: 0 })
    $('.projects-search-results ul')
      .empty()
      .html('
⌒
')
    window.simplesearchUtil.next()
  })
}

$(document).ready(function () {
  $('.button-collapse').sideNav()

  shuffleStrategies()

  if (typeof telescopicIntro === 'object') {
    initTelescopicText(telescopicIntro, '#telescopicText')
  }

  if (typeof recentArticles === 'object') {
    randomizeRecent(recentArticles, '#hero')
  }

  if (typeof authorLinks !== 'undefined') {
    var links = authorLinks
    if (typeof authorLinks === 'object' && !authorLinks.length) {
      links = []
      var keys = Object.keys(authorLinks)
      for (var i = 0; i < keys.length; i++) {
        links.push(authorLinks[keys[i]])
      }
    }
    initAuthorLinks(links, '#authorLinks')
  }

  if (pageId === 'projects') {
    initSearch()
  } else {
    $('.strategy-project-tags li:not(.bound)')
    .addClass('bound')
    .attr('role', 'button')
    .on('click', function () {
      var t = $(this).text().trim()
      t = t.indexOf(' ') === -1 ? t : ('"' + t + '"')
      window.location = '/projects?q=tags:' + t
    })
  }

  $('[data-author-img]').each(function (index, author) {
    var links = $(author).data('author-img').split('!$!')
    for (var i = 0; i < links.length; i++) {
      if (links[i].indexOf('github.com') !== -1) {
        $(author).attr('src', links[i] + '.png?size=200')
        break
      }
    }
  })

  $('#pintar').pushpin({
    top: 0,
    offset: 100
  })
})
<commit_msg>do not show features and strataegies<commit_after>/* global $, telescopicIntro, recentArticles, authorLinks, siteStrategies, SimpleSearch, pageId, siteAuthors */

var shuffleStrategies = function () {
  var li = $('.strategies-list > ul > li')
  li.detach().sort(function (a, b) {
    return parseInt(Math.random() * 10) % 2
  })
  $('.strategies-list > ul').empty().append(li)
}

var initTelescopicText = function (telescopicTextJSON, telescopicTextContainer) {
  var compose = function (textObject) {
    var story = []
    var keys = Object.keys(textObject)

    for (var i in keys) {
      var key1 = keys[i]
      var key2 = ''
      var lastIndex = key1.length - 1
      if (key1.indexOf(',') === lastIndex) {
        key1 = key1.substring(0, lastIndex)
        key2 = ','
      }
      var val = textObject[keys[i]]
      if (val === null || typeof val === 'undefined') {
        story.push('' + keys[i] + '')
      } else if (typeof val === 'string') {
        story.push('' + key1 + '' + key2)
      } else if (typeof val === 'object') {
        var chapter = compose(val)
        var id = i + '-' + keys[i].toLowerCase().replace(/([^A-Z0-9])+/ig, '')
        story.push('' + key1 + '' + key2 + '')
        story.push('' + chapter + '')
      }
    }

    return story.join(' ')
  }

  var textobj = typeof telescopicTextJSON === 'string' ? JSON.parse(telescopicTextJSON) : telescopicTextJSON
  var telescopicTextContent = compose(textobj)

  $(telescopicTextContainer).html(telescopicTextContent)
  var reset = $('')
  reset.on('click', function () {
    $('.telescopic-content:not(.hidden)').addClass('hidden')
    $('.telescopic-root.hidden').removeClass('hidden')
  })
  $(telescopicTextContainer).append(reset)

  $('span[data-controls].telescopic-link')
    .each(function () {
      $(this).on('click', function (event) {
        $(this).parent().addClass('hidden')
        $('#' + $(this).data('controls')).removeClass('hidden')
      })
    })
}

var getIconClass = function (service) {
  if (service) {
    var s = service.toLowerCase().replace(/\s/g, '')
    if (s.indexOf('github') > -1) {
      return 'fa fa-github'
    } else if (s.indexOf('twitter') > -1) {
      return 'fa fa-twitter'
    } else if (s.indexOf('stackoverflow') > -1) {
      return 'fa fa-stack-overflow'
    } else if (s.indexOf('medium') > -1) {
      return 'fa fa-medium'
    } else if (s.indexOf('linkedin') > -1) {
      return 'fa fa-linkedin'
    } else if (s.indexOf('http') === -1 && s.indexOf('@') > -1) {
      return 'fa fa-envelope'
    } else if (s.indexOf('http') > -1) {
      return 'fa fa-laptop'
    }
  }
}

var initAuthorLinks = function (authorLinks, authorLinksContainer) {
  var links = []
  var icon = null
  var mail = false
  var authorlinks = typeof authorLinks === 'string' ? JSON.parse(authorLinks) : authorLinks
  if (!authorlinks.length) {
    var keys = Object.keys(authorLinks)
    for (var i = 0; i < keys.length; i++) {
      icon = getIconClass(keys[i])
      mail = keys[i] && keys[i].indexOf('http') === -1 && keys[i].indexOf('@') > -1
      if (icon) {
        if (mail) {
          links.push('')
        } else {
          links.push('')
        }
      }
    }
  } else {
    for (var j = 0; j < authorLinks.length; j++) {
      icon = getIconClass(authorLinks[j])
      mail = authorLinks[j] && authorLinks[j].indexOf('http') === -1 && authorLinks[j].indexOf('@') > -1
      if (icon) {
        if (mail) {
          links.push('')
        } else {
          links.push('')
        }
      }
    }
  }

  $(authorLinksContainer).html(links)
}

var randomizeRecent = function (newArticles, recentContainer) {
  var hero = newArticles[Math.floor(Math.random() * 5)]
  var mainLink
  var pubName
  var icon

  if (hero.links['Compose Articles']) {
    mainLink = hero.links['Compose Articles']
    pubName = 'Compose.com/articles'
    icon = 'cubes'
  } else if (hero.links['Medium']) {
    mainLink = hero.links['Medium']
    pubName = 'Medium.com'
    icon = 'medium'
  } else if (hero.links['GitHub']) {
    mainLink = hero.links['GitHub']
    pubName = 'GitHub'
    icon = 'github'
  } else if (hero.links['Notebook']) {
    mainLink = hero.links['Notebook']
    pubName = 'Data Science Notebook'
    icon = 'book'
  } else if (hero.links['npm']) {
    mainLink = hero.links['npm']
    pubName = 'npmjs.com'
    icon = 'code'
  } else if (hero.links['dataset']) {
    mainLink = hero.links['dataset']
    pubName = 'Dataset'
    icon = 'database'
  } else {
    mainLink = hero.links[ Object.keys(hero.links)[0] ]
    pubName = 'External Site'
    icon = 'newspaper-o'
  }

  var html = `${hero.title}

${hero.headline}

    ${pubName}`

  $(recentContainer).append(html)
}

window.devadvo = {
  onBefore: function (q, options, paging, searchurl) {
    $('.projects-next, .projects-prev').prop('disabled', true)
  },

  searchOnData: function (results) {
    if (results.data.rows) {
      // grab first strategy reference
      results.data.rows = results.data.rows.map(function (row) {
        if (row.strategies && row.strategies.length && row.strategies[0] !== '__') {
          row.firstStrategy = {
            'name': siteStrategies[row.strategies[0]] || row.strategies[0],
            'link': row.strategies[0]
          }
        }
        return row
      })
      // filter unwanted facets
      if (window.devadvo.hiddenfacets) {
        window.devadvo.hiddenfacets.forEach(function (f) {
          if (results.data.counts.hasOwnProperty(f)) {
            delete results.data.counts[f]
          }
        })
      }
    }
    return results
  },

  searchOnSuccess: function (results) {
    if (results.data && results.data.rows && results.data.rows.length > 0) {
      var resultslist = $('.projects-search-results ul').empty()
      results.data.rows.forEach(function (project) {
        var tags = project.tags && project.tags.length ? ('
' + project.tags.join('

') + '
') : ''
        var linkkeys = Object.keys(project.links)
        var links = ''
        for (var i = 0; i < linkkeys.length; i++) {
          if (i > 0) {
            links += ' | '
          }
          links += `<a href="${project.links[linkkeys[i]]}" class="strategy-project-link" title="'${project.title} on ${linkkeys[i]}' ">${linkkeys[i]}</a>`
          if (i > 0) {
            links += ''
          }
        }

        var strategy = ''
        if (project.firstStrategy) {
          strategy = `<a href="${project.firstStrategy.link}" class="unheader" title="More on our '${project.firstStrategy.name}' strategy">${project.firstStrategy.name}</a>`
        }

        var projdate = ''
        // if (project.date) {
        //   projdate = strategy ? (' – ' + project.date) : project.date
        // }

        var temp = `

              ${strategy}${projdate}

              ${project.title}

              ${links}

 ${project.headline}

${tags}

`

        resultslist.append(temp)

        $('.strategy-project-tags li:not(.bound)')
          .addClass('bound')
          .attr('role', 'button')
          .on('click', function () {
            var t = $(this).text()
            t = 'tags:' + (t.indexOf(' ') === -1 ? t : ('"' + t + '"'))
            var q = window.simplesearchUtil.queryString(t)
            window.simplesearchUtil.search(q)
          })
      })

      if (results.paging) {
        $('.projects-prev').attr('disabled', !results.paging.bookmarks || results.paging.bookmarks.length <= 1)
        $('.projects-next').attr('disabled', results.paging.hasMore !== true)

        var total = results.data.total_rows
        var page = results.paging.bookmarks.length - 1
        var limit = results.paging.limit
        var start = (page * limit) + 1
        var end = start + limit - 1
        if (start < 1) { start = 1 }
        if (total < 1) { start = 0 }
        if (end > total) { end = total }
        if (start > end) { end = start }
        var currentpage = page + 1
        var lastpage = Math.floor(total / limit) + (total % limit > 0 ? 1 : 0)
        console.log(start, end, total, currentpage, lastpage)
        $('.projects-current').text(currentpage)
        $('.projects-last').text(lastpage)
      }
    }

    if (results.facets) {
      $('[data-search-facetkey="tags"]').parent().prependTo('.simplesearch-facets-list')
      $('.simplesearch-facet-key:not(.bound)')
        .addClass('bound')
        .on('click', function () {
          $(this)
            .toggleClass('expanded')
            .next('.simplesearch-facet-value-list')
            .slideToggle('slow')

          var facet = $(this).attr('data-search-facetkey')
          if ($(this).hasClass('expanded') && window.devadvo.expandedfacets.indexOf(facet) === -1) {
            window.devadvo.expandedfacets.push(facet)
          } else if (window.devadvo.expandedfacets.indexOf(facet) > -1) {
            window.devadvo.expandedfacets.splice(window.devadvo.expandedfacets.indexOf(facet), 1)
          }
        })

      if (results.paging && results.paging.query && results.paging.query !== '*:*') {
        var query = results.paging.query.split(' AND ')
        $('.simplesearch-facet-value-name').each(function () {
          var q = $(this).attr('data-search-query')
          if (query.indexOf(q) >= 0) {
            $(this)
              .addClass('filtered')
              .closest('.simplesearch-facet-value-list')
              .css('display', 'block')
              .prev('.simplesearch-facet-key')
              .addClass('expanded')
          }
        })
      }

      if (window.devadvo.expandedfacets.length) {
        window.devadvo.expandedfacets.forEach(function (facet) {
          $(`[data-search-facetkey="${facet}"]:not(.expanded)`)
            .toggleClass('expanded')
            .next('.simplesearch-facet-value-list')
            .css('display', 'block')
        })
      }

      $('[data-search-facetkey="lead"]').text('Authors')
      $('[data-search-query^="lead:"]').each(function () {
        $(this).text(siteAuthors[$(this).text()])
      })
    }
  }
}

var initSearch = function () {
  window.devadvo.hiddenfacets = ['features', 'strategies']
  window.devadvo.expandedfacets = ['tags']
  window.simplesearchUtil = new SimpleSearch('https://advo-projects.mybluemix.net', {
    onSuccess: window.devadvo.searchOnSuccess,
    onData: window.devadvo.searchOnData,
    onBefore: window.devadvo.onBefore
  }, {
    inputField: '#projects-search-input',
    searchButton: false,
    facetsList: '.projects-search-facets'
  }, {
    deepLinking: true,
    params: 'sort="-date<string>"'
  })

  $('.projects-prev').on('click', function () {
    $('html, body').animate({ scrollTop: 0 })
    $('.projects-search-results ul')
      .empty()
      .html('
⌒
')
    window.simplesearchUtil.prev()
  })
  $('.projects-next').on('click', function () {
    $('html, body').animate({ scrollTop: 0 })
    $('.projects-search-results ul')
      .empty()
      .html('
⌒
')
    window.simplesearchUtil.next()
  })
}

$(document).ready(function () {
  $('.button-collapse').sideNav()

  shuffleStrategies()

  if (typeof telescopicIntro === 'object') {
    initTelescopicText(telescopicIntro, '#telescopicText')
  }

  if (typeof recentArticles === 'object') {
    randomizeRecent(recentArticles, '#hero')
  }

  if (typeof authorLinks !== 'undefined') {
    var links = authorLinks
    if (typeof authorLinks === 'object' && !authorLinks.length) {
      links = []
      var keys = Object.keys(authorLinks)
      for (var i = 0; i < keys.length; i++) {
        links.push(authorLinks[keys[i]])
      }
    }
    initAuthorLinks(links, '#authorLinks')
  }

  if (pageId === 'projects') {
    initSearch()
  } else {
    $('.strategy-project-tags li:not(.bound)')
    .addClass('bound')
    .attr('role', 'button')
    .on('click', function () {
      var t = $(this).text().trim()
      t = t.indexOf(' ') === -1 ? t : ('"' + t + '"')
      window.location = '/projects?q=tags:' + t
    })
  }

  $('[data-author-img]').each(function (index, author) {
    var links = $(author).data('author-img').split('!$!')
    for (var i = 0; i < links.length; i++) {
      if (links[i].indexOf('github.com') !== -1) {
        $(author).attr('src', links[i] + '.png?size=200')
        break
      }
    }
  })

  $('#pintar').pushpin({
    top: 0,
    offset: 100
  })
})
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Shivan Kaul
 * Copyright 2015 Michael Williams
 *
 * 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.
 */
 var poi_index = 0;
 var current_poi = null;
 var new_poi = false;
 var firstTime = true;

// talk = ["How about?", "Or maybe:", "Perhaps this?", "Consider going here, I hear it's great:", "This place is awesome I hear:", "My best friend loves this place:"]

function set_poi(poi) {
	$("#place_of_interest").fadeIn(300);
	var name = trim(poi.name);
	var url = encodeURIComponent(name);
	$("#place_of_interest .place-name").html(name);
	var search = "http://www.yelp.ca/search?find_desc=" + url;
	$("#link-yelp").attr("href", search);
	current_poi = poi;
	new_poi = true;
	// document.getElementById("loading").innerHTML = talk[Math.floor(Math.random() * talk.length)];
	if (!firstTime) {
		var elem = document.getElementById('loading');
		if (elem != undefined) {
			elem.parentNode.removeChild(elem);
		}
	}

	firstTime = false
	setInterval('update_poi_image()', 500);
	calcRoute(poi.coords[0], poi.coords[1])
}

function image_error(image) {
	set_poi(next_poi()) // change poi
	return true;
}

function update_poi_image() {
	if (new_poi && current_poi){
		if(current_poi.imageURL){
			var image = $("#place_of_interest .place_image")
			image.attr("src", current_poi.imageURL);
			$("#place_of_interest .place_image").prop("hidden", false);
			new_poi = false;
		} else {
			$("#place_of_interest .place_image").prop("hidden", true);
		}
		set_footer_offset();
	}
}

function next_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index >= (poi_list.length - 1)) {
		return poi_list[poi_list.length - 1];
	} else {
		poi_index++;
		return poi_list[poi_index];
	}
}

function prev_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index <= 0) {
		return poi_list[0];
	} else {
		poi_index--;
		return poi_list[poi_index];
	}
}

function is_next_poi(){
	return (poi_index < poi_list.length - 1);
}

function is_prev_poi(){
	return (poi_list > 0);
}

function show_button(){
	var offset = 50+200 - Math.floor($("#book-button").height()/2);
	console.log("Uber Button offset is: " + offset);
	$("#book-button").css("margin-top", offset);
	$("#book-button").fadeIn();
}

$(document).keyup(function(e) {
	e = e||window.event;
	if (e.keyCode == "37") { //Left Arrow
		//set_poi(prev_poi());
		show_button();
	} else if (e.keyCode == "39") { //Right Arrow
		$("#place_of_interest").fadeOut( 300, function() {
			set_poi(next_poi());
		});
	}
});

function set_footer_offset() {
	var f = $("footer");
	if (window.innerHeight >= $(document).height()) {
		f.css("position", "fixed");
		f.css("bottom", "2%");
		f.css("left", "50%");
		f.css("transform", "translateX(-50%)");
	} else {
		f.css("position", "relative");
		f.css("bottom", "auto");
		f.css("left", "auto");
		f.css("transform", "none");
	}

}

$(window).resize(function() {
	set_footer_offset();
});

$(document).ready(function(){
	set_footer_offset();
	get_location();
});
<commit_msg>Fix for button offset<commit_after>/*
 * Copyright 2015 Shivan Kaul
 * Copyright 2015 Michael Williams
 *
 * 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.
 */
 var poi_index = 0;
 var current_poi = null;
 var new_poi = false;
 var firstTime = true;

// talk = ["How about?", "Or maybe:", "Perhaps this?", "Consider going here, I hear it's great:", "This place is awesome I hear:", "My best friend loves this place:"]

function set_poi(poi) {
	$("#place_of_interest").fadeIn(300);
	var name = trim(poi.name);
	var url = encodeURIComponent(name);
	$("#place_of_interest .place-name").html(name);
	var search = "http://www.yelp.ca/search?find_desc=" + url;
	$("#link-yelp").attr("href", search);
	current_poi = poi;
	new_poi = true;
	// document.getElementById("loading").innerHTML = talk[Math.floor(Math.random() * talk.length)];
	if (!firstTime) {
		var elem = document.getElementById('loading');
		if (elem != undefined) {
			elem.parentNode.removeChild(elem);
		}
	}

	firstTime = false
	setInterval('update_poi_image()', 500);
	calcRoute(poi.coords[0], poi.coords[1])
}

function image_error(image) {
	set_poi(next_poi()) // change poi
	return true;
}

function update_poi_image() {
	if (new_poi && current_poi){
		if(current_poi.imageURL){
			var image = $("#place_of_interest .place_image")
			image.attr("src", current_poi.imageURL);
			$("#place_of_interest .place_image").prop("hidden", false);
			new_poi = false;
		} else {
			$("#place_of_interest .place_image").prop("hidden", true);
		}
		set_footer_offset();
	}
}

function next_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index >= (poi_list.length - 1)) {
		return poi_list[poi_list.length - 1];
	} else {
		poi_index++;
		return poi_list[poi_index];
	}
}

function prev_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index <= 0) {
		return poi_list[0];
	} else {
		poi_index--;
		return poi_list[poi_index];
	}
}

function is_next_poi(){
	return (poi_index < poi_list.length - 1);
}

function is_prev_poi(){
	return (poi_list > 0);
}

function show_button(){
	var offset = 50+200 - Math.floor($("#book-button").height()/2);
	console.log("Uber Button offset is: " + offset);
	$("#book-button img").css("margin-top", offset);
	$("#book-button").fadeIn();
}

$(document).keyup(function(e) {
	e = e||window.event;
	if (e.keyCode == "37") { //Left Arrow
		//set_poi(prev_poi());
		show_button();
	} else if (e.keyCode == "39") { //Right Arrow
		$("#place_of_interest").fadeOut( 300, function() {
			set_poi(next_poi());
		});
	}
});

function set_footer_offset() {
	var f = $("footer");
	if (window.innerHeight >= $(document).height()) {
		f.css("position", "fixed");
		f.css("bottom", "2%");
		f.css("left", "50%");
		f.css("transform", "translateX(-50%)");
	} else {
		f.css("position", "relative");
		f.css("bottom", "auto");
		f.css("left", "auto");
		f.css("transform", "none");
	}

}

$(window).resize(function() {
	set_footer_offset();
});

$(document).ready(function(){
	set_footer_offset();
	get_location();
});
<|endoftext|>


<commit_before>$(function () {
  var columns = [
    {id: "id", name: "ID", field: "id", minWidth: 24, width: 24, editor: Slick.Editors.Text},
    {id: "title", name: "書名", field: "title", width: 200, editor: Slick.Editors.Text},
    {id: "authors", name: "著者", field: "authors", width: 160, editor: Slick.Editors.Text},
    {id: "publisher", name: "出版社", field: "publisher", editor: Slick.Editors.Text},
    {id: "published-on", name: "出版日", field: "publishedOn", width: 70, editor: Slick.Editors.Text},
    {id: "book-type", name: "書籍種別", field: "bookType", width: 40, editor: Slick.Editors.Text},
    {id: "book-info-url", name: "書籍情報URL", field: "bookInfoURL", editor: Slick.Editors.Text},
    {id: "purchase-url", name: "購入URL", field: "purchaseURL", editor: Slick.Editors.Text},
    {id: "reading-status", name: "読書状態", field: "readingStatus", width: 46, editor: Slick.Editors.Text}
  ];

  var data = [
    {
      id: 1,
      title: "パーフェクトRuby",
      authors: "Rubyサポーターズ, すがわら まさのり, 寺田 玄太郎, 三村 益隆, 近藤 宇智朗, 橋立 友宏, 関口 亮一",
      publisher: "技術評論社",
      publishedOn: "2013-08-10",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4774158798",
      purchaseURL: "http://www.yodobashi.com/%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88Ruby-%E5%8D%98%E8%A1%8C%E6%9C%AC/pd/100000009001925295/",
      readingStatus: "読書中"
    },
    {
      id: 2,
      title: "Sphinxをはじめよう",
      authors: "清水川貴之, 小宮健, 山田剛, 若山史郎",
      publisher: "O'Reilly Japan",
      publishedOn: "2013-09-01",
      bookType: "mobi",
      bookInfoURL: "http://www.oreilly.co.jp/books/9784873116488/",
      purchaseURL: "http://www.oreilly.co.jp/books/9784873116488/",
      readingStatus: "読了"
    },
    {
      id: 3,
      title: "Programming in Lua, Third Edition",
      authors: "Roberto Ierusalimschy",
      publisher: "Lua.org",
      publishedOn: "2013-01-03",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/859037985X",
      purchaseURL: "http://store.feistyduck.com/products/programming-in-lua",
      readingStatus: "読了"
    },
    {
      id: 4,
      title: "Programming in Lua, Second Edition",
      authors: "Roberto Ierusalimschy",
      publisher: "Lua.org",
      publishedOn: "2006-03-05",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/8590379825",
      purchaseURL: "http://store.feistyduck.com/products/programming-in-lua",
      readingStatus: "読了"
    },
    {
      id: 5,
      title: "Programming Erlang: Software for a Concurrent World",
      authors: "Joe Armstrong",
      publisher: "Pragmatic Bookshelf",
      publishedOn: "2007-07-02",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/193435600X",
      purchaseURL: "http://pragprog.com/book/jaerlang/programming-erlang",
      readingStatus: "読了"
    },
    {
      id: 6,
      title: "Erlang Programming",
      authors: "Francesco Cesarini, Simon Thompson",
      publisher: "Oreilly & Associates Inc",
      publishedOn: "2009-06-26",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/0596518188",
      purchaseURL: "http://www.amazon.com/dp/B002L4EXHY",
      readingStatus: "読了"
    },
    {
      id: 7,
      title: "Agile Web Development with Rails 4",
      authors: "Sam Ruby",
      publisher: "Pragmatic Bookshelf",
      publishedOn: "2013-10-01",
      bookType: "mobi",
      bookInfoURL: "http://pragprog.com/book/rails4/agile-web-development-with-rails-4",
      purchaseURL: "http://pragprog.com/book/rails4/agile-web-development-with-rails-4",
      readingStatus: "読了"
    },
    {
      id: 8,
      title: "入門Chef Solo - Infrastructure as Code",
      authors: "伊藤直也",
      publisher: "伊藤直也",
      publishedOn: "2013-03-11",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00BSPH158",
      purchaseURL: "http://www.amazon.co.jp/dp/B00BSPH158",
      readingStatus: "読了"
    },
    {
      id: 9,
      title: "実践Vim",
      authors: "Drew Neil, 新丈径(翻訳)",
      publisher: "KADOKAWA/アスキー・メディアワークス",
      publishedOn: "2013-08-30",
      bookType: "pdf",
      bookInfoURL: "http://www.amazon.co.jp/dp/4048916599",
      purchaseURL: "http://tatsu-zine.com/books/practical-vim",
      readingStatus: "読了"
    },
    {
      id: 10,
      title: "ガベージコレクションのアルゴリズムと実装",
      authors: "中村 成洋, 相川 光, 竹内 郁雄(監修)",
      publisher: "秀和システム",
      publishedOn: "2013-03-18",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4798025623",
      purchaseURL: "http://tatsu-zine.com/books/gcbook",
      readingStatus: "読書中"
    },
    {
      id: 11,
      title: "エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド",
      authors: "坂本一樹",
      publisher: "インプレスジャパン",
      publishedOn: "2011-11-18",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844331094",
      purchaseURL: "http://tatsu-zine.com/books/objc",
      readingStatus: "読了"
    },
    {
      id: 12,
      title: "Effective Android",
      authors: "TechBooster",
      publisher: "インプレスジャパン",
      publishedOn: "2013-08-29",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844335340",
      purchaseURL: "http://tatsu-zine.com/books/effective-android",
      readingStatus: "読了"
    },
    {
      id: 13,
      title: "上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編",
      authors: "加藤 寛人 (著), 西方 夏子 (著), 藤川 宏之 (著), 鈴木 晃 (著), 高丘 知央 (著), 丸山 弘詩 (編集)",
      publisher: "インプレスジャパン",
      publishedOn: "2013-12-20",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844335200",
      purchaseURL: "http://www.amazon.co.jp/dp/4844335200",
      readingStatus: "読書中"
    },
    {
      id: 14,
      title: "詳解 Objective-C 2.0 第3版",
      authors: "荻原 剛志",
      publisher: "ソフトバンククリエイティブ",
      publishedOn: "2011-12-28",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4797368276",
      purchaseURL: "http://www.amazon.co.jp/dp/4797368276",
      readingStatus: "読了"
    },
    {
      id: 15,
      title: "iPhoneアプリ開発エキスパートガイド iOS 6対応",
      authors: "加藤 寛人 (著), 藤川 宏之 (著), 高丘 知央 (著), 西方 夏子 (著), 吉田 悠一 (著), 関川 雄介 (著), 丸山 弘詩 (編集)",
      publisher: "インプレスジャパン",
      publishedOn: "2013-04-04",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844333852",
      purchaseURL: "http://www.amazon.co.jp/dp/4844333852",
      readingStatus: "読了"
    },
    {
      id: 16,
      title: "iPhone/iPad グラフィックスプログラミング",
      authors: "STUDIO SHIN (著)",
      publisher: "ソフトバンククリエイティブ",
      publishedOn: "2013-05-28",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/479735819X",
      purchaseURL: "http://www.amazon.co.jp/dp/479735819X",
      readingStatus: "読了"
    },
    {
      id: 17,
      title: "iOS Core Frameworksテクニカルガイド",
      authors: "Shawn Welch (著), 柴田 文彦 (翻訳)",
      publisher: "インプレスジャパン",
      publishedOn: "2012-09-26",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844332724",
      purchaseURL: "http://www.amazon.co.jp/dp/4844332724",
      readingStatus: "読了"
    },
    {
      id: 18,
      title: "iOS 7 App Development Essentials",
      authors: "Neil Smyth",
      publisher: "Amazon Services International, Inc.",
      publishedOn: "2013-10-09",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00FPT5BKK",
      purchaseURL: "http://www.amazon.co.jp/dp/B00FPT5BKK",
      readingStatus: "読了"
    },
    {
      id: 19,
      title: "The Nature of Code",
      authors: "Daniel Shiffman",
      publisher: "Amazon Services International, Inc.",
      publishedOn: "2012-12-13",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00BPFT8D4",
      purchaseURL: "http://www.amazon.co.jp/dp/B00BPFT8D4",
      readingStatus: "読了"
    },
    {
      id: 20,
      title: "Working With Unix Processes",
      authors: "Jesse Storimer",
      publisher: "Amazon Services International, Inc.",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B0078VSRUE",
      purchaseURL: "http://www.amazon.co.jp/dp/B0078VSRUE",
      readingStatus: "読書中"
    }
  ];

  var options = {
    editable: true
  };

  var grid = new Slick.Grid("#myGrid", data, columns, options);
});
<commit_msg>Add one-column sort and adjust column widths.<commit_after>$(function () {
  var columns = [
    {id: "id", name: "ID", field: "id", width: 40, sortable: true, editor: Slick.Editors.Text},
    {id: "title", name: "書名", field: "title", width: 200, sortable: true, editor: Slick.Editors.Text},
    {id: "authors", name: "著者", field: "authors", width: 160, sortable: true, editor: Slick.Editors.Text},
    {id: "publisher", name: "出版社", field: "publisher", sortable: true, editor: Slick.Editors.Text},
    {id: "published-on", name: "出版日", field: "publishedOn", width: 70, sortable: true, editor: Slick.Editors.Text},
    {id: "book-type", name: "書籍種別", field: "bookType", width: 70, sortable: true, editor: Slick.Editors.Text},
    {id: "book-info-url", name: "書籍情報URL", field: "bookInfoURL", width: 90, sortable: true, editor: Slick.Editors.Text},
    {id: "purchase-url", name: "購入URL", field: "purchaseURL", sortable: true, editor: Slick.Editors.Text},
    {id: "reading-status", name: "読書状態", field: "readingStatus", width: 70, sortable: true, editor: Slick.Editors.Text}
  ];

  var data = [
    {
      id: 1,
      title: "パーフェクトRuby",
      authors: "Rubyサポーターズ, すがわら まさのり, 寺田 玄太郎, 三村 益隆, 近藤 宇智朗, 橋立 友宏, 関口 亮一",
      publisher: "技術評論社",
      publishedOn: "2013-08-10",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4774158798",
      purchaseURL: "http://www.yodobashi.com/%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88Ruby-%E5%8D%98%E8%A1%8C%E6%9C%AC/pd/100000009001925295/",
      readingStatus: "読書中"
    },
    {
      id: 2,
      title: "Sphinxをはじめよう",
      authors: "清水川貴之, 小宮健, 山田剛, 若山史郎",
      publisher: "O'Reilly Japan",
      publishedOn: "2013-09-01",
      bookType: "mobi",
      bookInfoURL: "http://www.oreilly.co.jp/books/9784873116488/",
      purchaseURL: "http://www.oreilly.co.jp/books/9784873116488/",
      readingStatus: "読了"
    },
    {
      id: 3,
      title: "Programming in Lua, Third Edition",
      authors: "Roberto Ierusalimschy",
      publisher: "Lua.org",
      publishedOn: "2013-01-03",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/859037985X",
      purchaseURL: "http://store.feistyduck.com/products/programming-in-lua",
      readingStatus: "読了"
    },
    {
      id: 4,
      title: "Programming in Lua, Second Edition",
      authors: "Roberto Ierusalimschy",
      publisher: "Lua.org",
      publishedOn: "2006-03-05",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/8590379825",
      purchaseURL: "http://store.feistyduck.com/products/programming-in-lua",
      readingStatus: "読了"
    },
    {
      id: 5,
      title: "Programming Erlang: Software for a Concurrent World",
      authors: "Joe Armstrong",
      publisher: "Pragmatic Bookshelf",
      publishedOn: "2007-07-02",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/193435600X",
      purchaseURL: "http://pragprog.com/book/jaerlang/programming-erlang",
      readingStatus: "読了"
    },
    {
      id: 6,
      title: "Erlang Programming",
      authors: "Francesco Cesarini, Simon Thompson",
      publisher: "Oreilly & Associates Inc",
      publishedOn: "2009-06-26",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/0596518188",
      purchaseURL: "http://www.amazon.com/dp/B002L4EXHY",
      readingStatus: "読了"
    },
    {
      id: 7,
      title: "Agile Web Development with Rails 4",
      authors: "Sam Ruby",
      publisher: "Pragmatic Bookshelf",
      publishedOn: "2013-10-01",
      bookType: "mobi",
      bookInfoURL: "http://pragprog.com/book/rails4/agile-web-development-with-rails-4",
      purchaseURL: "http://pragprog.com/book/rails4/agile-web-development-with-rails-4",
      readingStatus: "読了"
    },
    {
      id: 8,
      title: "入門Chef Solo - Infrastructure as Code",
      authors: "伊藤直也",
      publisher: "伊藤直也",
      publishedOn: "2013-03-11",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00BSPH158",
      purchaseURL: "http://www.amazon.co.jp/dp/B00BSPH158",
      readingStatus: "読了"
    },
    {
      id: 9,
      title: "実践Vim",
      authors: "Drew Neil, 新丈径(翻訳)",
      publisher: "KADOKAWA/アスキー・メディアワークス",
      publishedOn: "2013-08-30",
      bookType: "pdf",
      bookInfoURL: "http://www.amazon.co.jp/dp/4048916599",
      purchaseURL: "http://tatsu-zine.com/books/practical-vim",
      readingStatus: "読了"
    },
    {
      id: 10,
      title: "ガベージコレクションのアルゴリズムと実装",
      authors: "中村 成洋, 相川 光, 竹内 郁雄(監修)",
      publisher: "秀和システム",
      publishedOn: "2013-03-18",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4798025623",
      purchaseURL: "http://tatsu-zine.com/books/gcbook",
      readingStatus: "読書中"
    },
    {
      id: 11,
      title: "エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド",
      authors: "坂本一樹",
      publisher: "インプレスジャパン",
      publishedOn: "2011-11-18",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844331094",
      purchaseURL: "http://tatsu-zine.com/books/objc",
      readingStatus: "読了"
    },
    {
      id: 12,
      title: "Effective Android",
      authors: "TechBooster",
      publisher: "インプレスジャパン",
      publishedOn: "2013-08-29",
      bookType: "epub",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844335340",
      purchaseURL: "http://tatsu-zine.com/books/effective-android",
      readingStatus: "読了"
    },
    {
      id: 13,
      title: "上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編",
      authors: "加藤 寛人 (著), 西方 夏子 (著), 藤川 宏之 (著), 鈴木 晃 (著), 高丘 知央 (著), 丸山 弘詩 (編集)",
      publisher: "インプレスジャパン",
      publishedOn: "2013-12-20",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844335200",
      purchaseURL: "http://www.amazon.co.jp/dp/4844335200",
      readingStatus: "読書中"
    },
    {
      id: 14,
      title: "詳解 Objective-C 2.0 第3版",
      authors: "荻原 剛志",
      publisher: "ソフトバンククリエイティブ",
      publishedOn: "2011-12-28",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4797368276",
      purchaseURL: "http://www.amazon.co.jp/dp/4797368276",
      readingStatus: "読了"
    },
    {
      id: 15,
      title: "iPhoneアプリ開発エキスパートガイド iOS 6対応",
      authors: "加藤 寛人 (著), 藤川 宏之 (著), 高丘 知央 (著), 西方 夏子 (著), 吉田 悠一 (著), 関川 雄介 (著), 丸山 弘詩 (編集)",
      publisher: "インプレスジャパン",
      publishedOn: "2013-04-04",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844333852",
      purchaseURL: "http://www.amazon.co.jp/dp/4844333852",
      readingStatus: "読了"
    },
    {
      id: 16,
      title: "iPhone/iPad グラフィックスプログラミング",
      authors: "STUDIO SHIN (著)",
      publisher: "ソフトバンククリエイティブ",
      publishedOn: "2013-05-28",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/479735819X",
      purchaseURL: "http://www.amazon.co.jp/dp/479735819X",
      readingStatus: "読了"
    },
    {
      id: 17,
      title: "iOS Core Frameworksテクニカルガイド",
      authors: "Shawn Welch (著), 柴田 文彦 (翻訳)",
      publisher: "インプレスジャパン",
      publishedOn: "2012-09-26",
      bookType: "紙",
      bookInfoURL: "http://www.amazon.co.jp/dp/4844332724",
      purchaseURL: "http://www.amazon.co.jp/dp/4844332724",
      readingStatus: "読了"
    },
    {
      id: 18,
      title: "iOS 7 App Development Essentials",
      authors: "Neil Smyth",
      publisher: "Amazon Services International, Inc.",
      publishedOn: "2013-10-09",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00FPT5BKK",
      purchaseURL: "http://www.amazon.co.jp/dp/B00FPT5BKK",
      readingStatus: "読了"
    },
    {
      id: 19,
      title: "The Nature of Code",
      authors: "Daniel Shiffman",
      publisher: "Amazon Services International, Inc.",
      publishedOn: "2012-12-13",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B00BPFT8D4",
      purchaseURL: "http://www.amazon.co.jp/dp/B00BPFT8D4",
      readingStatus: "読了"
    },
    {
      id: 20,
      title: "Working With Unix Processes",
      authors: "Jesse Storimer",
      publisher: "Amazon Services International, Inc.",
      bookType: "mobi",
      bookInfoURL: "http://www.amazon.co.jp/dp/B0078VSRUE",
      purchaseURL: "http://www.amazon.co.jp/dp/B0078VSRUE",
      readingStatus: "読書中"
    }
  ];

  var options = {
    editable: true
  };

  var grid = new Slick.Grid("#myGrid", data, columns, options);
  grid.onSort.subscribe(function(e, args) {
    var cols = [args];

    data.sort(function (dataRow1, dataRow2) {
      for (var i = 0, l = cols.length; i < l; i++) {
        var field = cols[i].sortCol.field;
        var sign = cols[i].sortAsc ? 1 : -1;
        var value1 = dataRow1[field], value2 = dataRow2[field];
        var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
        if (result != 0) {
          return result;
        }
      }
      return 0;
    });
    grid.invalidate();
    grid.render();
  });
});
<|endoftext|>


<commit_before>// take input string
// create empty output string
// remove last letter of input string
// add that letter to empty string
// keep doing this until the input string is empty
// return output string

function reverse(str) {
  var newString = ""
  for (var i = str.length; i > 0, i--)
    newString += str[i]
}<commit_msg>Add driver code to test function<commit_after>// take input string
// create empty output string
// get the last letter of the input string
// add that letter to empty string
// keep doing this until we've gone through the entire input string
// return output string

function reverse(str) {

  var newString = "";

  for (var i = str.length-1; i >= 0; i--) {
    newString += str[i]
  }

  console.log(newString);
  return newString
}

// driver code to test program

var test1 = reverse("Ellie");
var test2 = reverse("elephant");
var test3 = reverse("unencumbered");

console.log(test1)

if (1==1) {
  console.log(test2)
}<|endoftext|>


<commit_before>// Implement sitewide search for Apostrophe. Provides the
// `apostrophe-search` page type for the `/search` page, which
// you should include in your "parked pages" if you wish
// to have one (see [apostrophe-pages](../apostrophe-pages/index.html)).
//
// Search is powered by the full-text search features of MongoDB.

var _ = require('@sailshq/lodash');
var async = require('async');

module.exports = {

  alias: 'search',

  extend: 'apostrophe-custom-pages',

  name: 'apostrophe-search',

  perPage: 10,

  afterConstruct: function(self) {
    self.dispatchAll();
    self.enableFilters();
    self.pushAssets();
    self.apos.tasks.add(self.__meta.name, 'index', self.indexTask);
  },

  construct: function(self, options) {

    self.perPage = options.perPage;

    if (options.suggestions === undefined) {
      // bc fallback, not great
      options.suggestions = {
        url: '/search'
      };
    } else if (options.suggestions === true) {
      // will catch the new, better standard route URL
      options.suggestions = {};
    } else {
      // already an object
    }
    options.suggestions.url = options.suggestions.url || self.action + '/suggest';

    require('./lib/browser.js')(self, options);

    self.modulesReady = function(callback) {
      return self.determineTypes(callback);
    };

    self.determineTypes = function(callback) {
      self.types = self.options.types || _.pluck(self.apos.pages.typeChoices, 'name');
      if (self.options.types) {
        // Explicit configuration was chosen
        return setImmediate(callback);
      }
      return self.callAllAndEmit('searchDetermineTypes', 'determineTypes', self.types, callback);
    };

    self.enableFilters = function() {
      if (self.options.filters) {
        self.filters = self.options.filters;
        if (!_.find(self.filters, { name: '__else' })) {
          self.filters = self.options.filters.concat(
            [
              {
                label: "Everything Else",
                name: "__else"
              }
            ]
          );
        }
      }
    };

    self.route('get', 'suggest', function(req, res) {
      return self.suggest(req, self.apos.launder.string(req.query.q)).then(function(docs) {
        return res.send(self.render(req, 'suggest', { docs: docs }));
      }).catch(function(err) {
        self.apos.utils.error(err);
        return res.status(500).send('');
      });
    });

    self.suggest = function(req, q) {
      return self.apos.docs.find(req, {}, { _url: 1, title: 1 })
        .limit((self.options.suggest && self.options.suggest.limit) || 10)
        .search(q)
        .toArray();
    };

    // This method implements the search results page. It populates `req.data.docs`
    // and provides pagination via `req.data.currentPage` and `req.data.totalPages`,
    // not to be confused with `req.data.totalDocs` which is the total number of
    // documents matching the search. The filters configured for the module are
    // respected.

    self.indexPage = function(req, callback) {

      // Finesse so we can use queryToFilters but we still support q, which is
      // a common expectation/preference
      req.query.search = req.query.search || req.query.q;

      // Cope with filters
      var allowedTypes;

      var defaultingToAll = false;

      var cursor = self.apos.docs.find(req, {})
        .queryToFilters(req.query, 'public')
        .perPage(self.perPage);
      if (self.filters) {
        var filterTypes = _.filter(
          _.pluck(self.filters, 'name'),
          function(name) {
            return name !== '__else';
          }
        );
        allowedTypes = _.filter(self.types, function(name) {
          return _.has(req.query, name);
        });
        if (req.query.__else) {
          allowedTypes = allowedTypes.concat(_.difference(self.types, filterTypes));
        }
        if (!allowedTypes.length) {
          // Default is everything
          defaultingToAll = true;
          allowedTypes = self.types;
        }
      } else {
        allowedTypes = self.types;
      }
      cursor.and({ type: { $in: allowedTypes } });

      var docs = [];

      if (self.filters) {
        req.data.filters = _.cloneDeep(self.filters);

        _.each(req.data.filters, function(filter) {
          if (defaultingToAll || req.query[filter.name]) {
            filter.value = true;
          }
        });
      }

      return async.series([ totalDocs, findDocs ], function(err) {

        if (err) {
          return callback(err);
        }

        if (self.apos.utils.isAjaxRequest(req)) {
          req.template = self.renderer('indexAjax');
        } else {
          req.template = self.renderer('index');
        }
        req.data.currentPage = cursor.get('page');
        req.data.docs = docs;

        return self.beforeIndex(req, callback);
      });

      function totalDocs(callback) {
        return cursor.toCount(function(err, count) {
          if (err) {
            return callback(err);
          }
          if (cursor.get('page') > cursor.get('totalPages')) {
            req.notFound = true;
            return callback(null);
          }
          req.data.totalDocs = count;
          req.data.totalPages = cursor.get('totalPages');
          return callback();
        });
      }

      function findDocs(callback) {

        // Polymorphic find: fetch just the ids at first, then go back
        // and fetch them via their own type managers so that we get the
        // expected joins and urls and suchlike.

        var idsAndTypes;
        var byType;

        return async.series([
          getIdsAndTypes,
          getDocs
        ], callback);

        function getIdsAndTypes(callback) {
          return cursor.projection({ _id: 1, type: 1 }).toArray(function(err, _idsAndTypes) {
            if (err) {
              return callback(err);
            }
            idsAndTypes = _idsAndTypes;
            return callback(null);
          });
        }

        function getDocs(callback) {
          byType = _.groupBy(idsAndTypes, 'type');
          return async.eachSeries(_.keys(byType), getDocsOfType, function(err) {
            if (err) {
              return callback(err);
            }
            // Restore the intended order ($in doesn't respect it and neither does
            // fetching them all by type). ACHTUNG: without this search quality goes
            // right out the window. -Tom
            docs = self.apos.utils.orderById(_.pluck(idsAndTypes, '_id'), docs);
            return callback(null);
          });
        }

        function getDocsOfType(type, callback) {
          var manager = self.apos.docs.getManager(type);
          if (!manager) {
            return setImmediate(callback);
          }
          return manager.find(req,
            {
              type: type,
              _id: {
                $in: _.pluck(byType[type], '_id')
              }
            }
          ).toArray(function(err, docsOfType) {
            if (err) {
              return callback(err);
            }
            docs = docs.concat(docsOfType);
            return callback(null);
          });
        }

      }

    };

    // Called before each page of search results is rendered; override hook

    self.beforeIndex = function(req, callback) {
      return setImmediate(callback);
    };

    self.dispatchAll = function() {
      self.dispatch('/', self.indexPage);
    };

    // Implementation of search indexing as documents are saved. Invoked
    // via callAll by the docs module

    self.docBeforeSave = function(req, doc, options) {
      return self.indexDoc(req, doc);
    };

    // Index one doc for participation in search

    self.indexDoc = function(req, doc) {

      var texts = self.getSearchTexts(doc);

      _.each(texts, function(text) {
        if (text.text === undefined) {
          text.text = '';
        }
      });

      var highTexts = _.filter(texts, function(text) {
        return text.weight > 10;
      });

      var searchSummary = _.map(_.filter(texts, function(text) { return !text.silent; }), function(text) { return text.text; }).join(" ");
      var highText = self.boilTexts(highTexts);
      var lowText = self.boilTexts(texts);
      var titleSortified = self.apos.utils.sortify(doc.title);
      var highWords = _.uniq(highText.split(/ /));

      // merge our doc with its various search texts
      _.assign(doc, {
        titleSortified: titleSortified,
        highSearchText: highText,
        highSearchWords: highWords,
        lowSearchText: lowText,
        searchSummary: searchSummary
      });
    };

    // Implements the `apostrophe-search:index` task, which re-indexes all pages.
    // This should only be needed if you have changed your mind about the
    // `searchable` property for various schema fields. Indexing is automatic
    // every time a doc is saved

    self.indexTask = function(apos, argv, callback) {
      var req = self.apos.tasks.getReq();
      return self.apos.migrations.eachDoc({}, _.partial(self.indexTaskOne, req), callback);
    };

    // Indexes just one document as part of the implementation of the
    // `apostrophe-search:index` task. This isn't the method you want to
    // override. See `indexDoc` and `getSearchTexts`

    self.indexTaskOne = function(req, doc, callback) {
      self.indexDoc(req, doc);
      return self.apos.docs.db.update({ _id: doc._id }, doc, callback);
    };

    // Returns texts which are a reasonable basis for
    // generating search results for this page. Should return
    // an array in which each entry is an object with
    // 'weight' and 'text' properties. 'weight' is a measure
    // of relative importance. 'text' is the text associated
    // with that chunk of content.

    self.getSearchTexts = function(doc) {
      var texts = [];
      // Shown separately, so don't include it in the summary
      texts.push({ weight: 100, text: doc.title, silent: true });
      // Usually redundant to the text of the page, so don't
      // show it in the description, but it's highly-weighted stuff
      // because we use it as the summary in a google search
      // result
      texts.push({ weight: 100, text: doc.seoDescription, silent: true });
      // The slug often reveals more useful search-targeting information
      texts.push({ weight: 100, text: doc.slug, silent: true });
      // Not great to include in the summary either
      texts.push({ weight: 100, text: (doc.tags || []).join("\n"), silent: true });

      // This event is an opportunity to add custom texts for
      // various types of pages
      self.apos.emit('docSearchIndex', doc, texts);

      // Areas can be schemaless so find them automatically
      self.apos.areas.walk(doc, function(area, dotPath) {
        // Do not examine areas accessed via temporarily
        // joined information, such as snippets in a snippet
        // widget. Allow those items to be found on their
        // own as search results, and avoid bloating the
        // search text up to the 16MB limit as happened on DR
        if (dotPath.match(/\._\w/)) {
          return;
        }
        _.each(area.items, function(item) {
          var manager = self.apos.areas.getWidgetManager(item.type);
          if (!manager) {
            self.apos.areas.warnMissingWidgetType(item.type);
            return;
          }
          if (manager.addSearchTexts) {
            manager.addSearchTexts(item, texts);
          }
        });
      });

      return texts;
    };

    // Reduces array of texts to a single space-separated string, passes the result
    // through apos.utils.sortify to eliminate unwanted characters and case differences

    self.boilTexts = function(texts) {
      var text = _.reduce(texts, function(memo, text) {
        return memo + ' ' + text.text;
      }, '');
      text = self.apos.utils.sortify(text);
      return text;
    };

    // Invoked by the apostrophe-versions module.
    // Identify fields that should never be rolled back

    self.docUnversionedFields = function(req, doc, fields) {
      fields.push('titleSortified', 'highSearchText', 'highSearchWords', 'lowSearchText', 'searchSummary');
    };

  }
};
<commit_msg>documentation for this feature, and for other options<commit_after>// Implement sitewide search for Apostrophe. Provides the
// `apostrophe-search` page type for the `/search` page, which
// you should include in your "parked pages" if you wish
// to have one (see [apostrophe-pages](../apostrophe-pages/index.html)).
//
// Search is powered by the full-text search features of MongoDB.
//
// ## Options
//
// ### `perPage`: search results per results page. Defaults to 10.
//
// ### `suggestions`: if `suggestions` is `true`, and the `notFound.html`
// template of `apostrophe-pages` contains this element:
//
// `<div data-apos-notfound-search-results></div>`
//
// Apostrophe will attempt to locate relevant pages by feeding the component
// words of the URL to the search engine, and display those suggestions.
//
// If `suggestions` is explicitly `false`, this does not happen.
//
// If `suggestions` is an object, this feature is enabled and the `limit`
// suboption may optionally be changed to a value other than `10`.
//
// For legacy reasons, if `suggestions` is not set at all, the feature
// still operates but attempts to obtain suggestions from `/search`. This
// will work adequately if you have an Apostrophe sitewide search page
// at that URL, but we recommend you set `suggestions: true` instead.
// This allows you to override `suggest.html` to customize the behavior,
// and also improves performance by using a simpler query for the 404
// suggestions.
//
// `types`: an array of page and piece doc type names allowed to be included
// in search results. If not present, this is determined programmatically.
// In the latter case, the `searchDetermineTypes` callAll method and the
// `determineTypes` promise event are fired. Implementations of these
// take an array argument and push new type names on it. `apostrophe-pieces` modules
// monitor this and add their `name`, or do not, based on their `searchable` option.
//
// `filters`: an array of filters to be offered to the user, each of which
// is an object with a `name` property and a `label` property. If no
// entry has the name `__else`, an "Everything Else" filter is automatically
// added. This is because there are always page types and piece types that
// are edge cases not relevant enough to explicitly offer a filter for, but
// which should nevertheless be included in results.

var _ = require('@sailshq/lodash');
var async = require('async');

module.exports = {

  alias: 'search',

  extend: 'apostrophe-custom-pages',

  name: 'apostrophe-search',

  perPage: 10,

  afterConstruct: function(self) {
    self.dispatchAll();
    self.enableFilters();
    self.pushAssets();
    self.apos.tasks.add(self.__meta.name, 'index', self.indexTask);
  },

  construct: function(self, options) {

    self.perPage = options.perPage;

    if (options.suggestions === undefined) {
      // bc fallback, not great
      options.suggestions = {
        url: '/search'
      };
    } else if (options.suggestions === true) {
      // will catch the new, better standard route URL
      options.suggestions = {};
    } else {
      // already an object
    }
    options.suggestions.url = options.suggestions.url || self.action + '/suggest';

    require('./lib/browser.js')(self, options);

    self.modulesReady = function(callback) {
      return self.determineTypes(callback);
    };

    self.determineTypes = function(callback) {
      self.types = self.options.types || _.pluck(self.apos.pages.typeChoices, 'name');
      if (self.options.types) {
        // Explicit configuration was chosen
        return setImmediate(callback);
      }
      return self.callAllAndEmit('searchDetermineTypes', 'determineTypes', self.types, callback);
    };

    self.enableFilters = function() {
      if (self.options.filters) {
        self.filters = self.options.filters;
        if (!_.find(self.filters, { name: '__else' })) {
          self.filters = self.options.filters.concat(
            [
              {
                label: "Everything Else",
                name: "__else"
              }
            ]
          );
        }
      }
    };

    self.route('get', 'suggest', function(req, res) {
      return self.suggest(req, self.apos.launder.string(req.query.q)).then(function(docs) {
        return res.send(self.render(req, 'suggest', { docs: docs }));
      }).catch(function(err) {
        self.apos.utils.error(err);
        return res.status(500).send('');
      });
    });

    self.suggest = function(req, q) {
      return self.apos.docs.find(req, {}, { _url: 1, title: 1 })
        .limit((self.options.suggestions && self.options.suggestions.limit) || 10)
        .search(q)
        .toArray();
    };

    // This method implements the search results page. It populates `req.data.docs`
    // and provides pagination via `req.data.currentPage` and `req.data.totalPages`,
    // not to be confused with `req.data.totalDocs` which is the total number of
    // documents matching the search. The filters configured for the module are
    // respected.

    self.indexPage = function(req, callback) {

      // Finesse so we can use queryToFilters but we still support q, which is
      // a common expectation/preference
      req.query.search = req.query.search || req.query.q;

      // Cope with filters
      var allowedTypes;

      var defaultingToAll = false;

      var cursor = self.apos.docs.find(req, {})
        .queryToFilters(req.query, 'public')
        .perPage(self.perPage);
      if (self.filters) {
        var filterTypes = _.filter(
          _.pluck(self.filters, 'name'),
          function(name) {
            return name !== '__else';
          }
        );
        allowedTypes = _.filter(self.types, function(name) {
          return _.has(req.query, name);
        });
        if (req.query.__else) {
          allowedTypes = allowedTypes.concat(_.difference(self.types, filterTypes));
        }
        if (!allowedTypes.length) {
          // Default is everything
          defaultingToAll = true;
          allowedTypes = self.types;
        }
      } else {
        allowedTypes = self.types;
      }
      cursor.and({ type: { $in: allowedTypes } });

      var docs = [];

      if (self.filters) {
        req.data.filters = _.cloneDeep(self.filters);

        _.each(req.data.filters, function(filter) {
          if (defaultingToAll || req.query[filter.name]) {
            filter.value = true;
          }
        });
      }

      return async.series([ totalDocs, findDocs ], function(err) {

        if (err) {
          return callback(err);
        }

        if (self.apos.utils.isAjaxRequest(req)) {
          req.template = self.renderer('indexAjax');
        } else {
          req.template = self.renderer('index');
        }
        req.data.currentPage = cursor.get('page');
        req.data.docs = docs;

        return self.beforeIndex(req, callback);
      });

      function totalDocs(callback) {
        return cursor.toCount(function(err, count) {
          if (err) {
            return callback(err);
          }
          if (cursor.get('page') > cursor.get('totalPages')) {
            req.notFound = true;
            return callback(null);
          }
          req.data.totalDocs = count;
          req.data.totalPages = cursor.get('totalPages');
          return callback();
        });
      }

      function findDocs(callback) {

        // Polymorphic find: fetch just the ids at first, then go back
        // and fetch them via their own type managers so that we get the
        // expected joins and urls and suchlike.

        var idsAndTypes;
        var byType;

        return async.series([
          getIdsAndTypes,
          getDocs
        ], callback);

        function getIdsAndTypes(callback) {
          return cursor.projection({ _id: 1, type: 1 }).toArray(function(err, _idsAndTypes) {
            if (err) {
              return callback(err);
            }
            idsAndTypes = _idsAndTypes;
            return callback(null);
          });
        }

        function getDocs(callback) {
          byType = _.groupBy(idsAndTypes, 'type');
          return async.eachSeries(_.keys(byType), getDocsOfType, function(err) {
            if (err) {
              return callback(err);
            }
            // Restore the intended order ($in doesn't respect it and neither does
            // fetching them all by type). ACHTUNG: without this search quality goes
            // right out the window. -Tom
            docs = self.apos.utils.orderById(_.pluck(idsAndTypes, '_id'), docs);
            return callback(null);
          });
        }

        function getDocsOfType(type, callback) {
          var manager = self.apos.docs.getManager(type);
          if (!manager) {
            return setImmediate(callback);
          }
          return manager.find(req,
            {
              type: type,
              _id: {
                $in: _.pluck(byType[type], '_id')
              }
            }
          ).toArray(function(err, docsOfType) {
            if (err) {
              return callback(err);
            }
            docs = docs.concat(docsOfType);
            return callback(null);
          });
        }

      }

    };

    // Called before each page of search results is rendered; override hook

    self.beforeIndex = function(req, callback) {
      return setImmediate(callback);
    };

    self.dispatchAll = function() {
      self.dispatch('/', self.indexPage);
    };

    // Implementation of search indexing as documents are saved. Invoked
    // via callAll by the docs module

    self.docBeforeSave = function(req, doc, options) {
      return self.indexDoc(req, doc);
    };

    // Index one doc for participation in search

    self.indexDoc = function(req, doc) {

      var texts = self.getSearchTexts(doc);

      _.each(texts, function(text) {
        if (text.text === undefined) {
          text.text = '';
        }
      });

      var highTexts = _.filter(texts, function(text) {
        return text.weight > 10;
      });

      var searchSummary = _.map(_.filter(texts, function(text) { return !text.silent; }), function(text) { return text.text; }).join(" ");
      var highText = self.boilTexts(highTexts);
      var lowText = self.boilTexts(texts);
      var titleSortified = self.apos.utils.sortify(doc.title);
      var highWords = _.uniq(highText.split(/ /));

      // merge our doc with its various search texts
      _.assign(doc, {
        titleSortified: titleSortified,
        highSearchText: highText,
        highSearchWords: highWords,
        lowSearchText: lowText,
        searchSummary: searchSummary
      });
    };

    // Implements the `apostrophe-search:index` task, which re-indexes all pages.
    // This should only be needed if you have changed your mind about the
    // `searchable` property for various schema fields. Indexing is automatic
    // every time a doc is saved

    self.indexTask = function(apos, argv, callback) {
      var req = self.apos.tasks.getReq();
      return self.apos.migrations.eachDoc({}, _.partial(self.indexTaskOne, req), callback);
    };

    // Indexes just one document as part of the implementation of the
    // `apostrophe-search:index` task. This isn't the method you want to
    // override. See `indexDoc` and `getSearchTexts`

    self.indexTaskOne = function(req, doc, callback) {
      self.indexDoc(req, doc);
      return self.apos.docs.db.update({ _id: doc._id }, doc, callback);
    };

    // Returns texts which are a reasonable basis for
    // generating search results for this page. Should return
    // an array in which each entry is an object with
    // 'weight' and 'text' properties. 'weight' is a measure
    // of relative importance. 'text' is the text associated
    // with that chunk of content.

    self.getSearchTexts = function(doc) {
      var texts = [];
      // Shown separately, so don't include it in the summary
      texts.push({ weight: 100, text: doc.title, silent: true });
      // Usually redundant to the text of the page, so don't
      // show it in the description, but it's highly-weighted stuff
      // because we use it as the summary in a google search
      // result
      texts.push({ weight: 100, text: doc.seoDescription, silent: true });
      // The slug often reveals more useful search-targeting information
      texts.push({ weight: 100, text: doc.slug, silent: true });
      // Not great to include in the summary either
      texts.push({ weight: 100, text: (doc.tags || []).join("\n"), silent: true });

      // This event is an opportunity to add custom texts for
      // various types of pages
      self.apos.emit('docSearchIndex', doc, texts);

      // Areas can be schemaless so find them automatically
      self.apos.areas.walk(doc, function(area, dotPath) {
        // Do not examine areas accessed via temporarily
        // joined information, such as snippets in a snippet
        // widget. Allow those items to be found on their
        // own as search results, and avoid bloating the
        // search text up to the 16MB limit as happened on DR
        if (dotPath.match(/\._\w/)) {
          return;
        }
        _.each(area.items, function(item) {
          var manager = self.apos.areas.getWidgetManager(item.type);
          if (!manager) {
            self.apos.areas.warnMissingWidgetType(item.type);
            return;
          }
          if (manager.addSearchTexts) {
            manager.addSearchTexts(item, texts);
          }
        });
      });

      return texts;
    };

    // Reduces array of texts to a single space-separated string, passes the result
    // through apos.utils.sortify to eliminate unwanted characters and case differences

    self.boilTexts = function(texts) {
      var text = _.reduce(texts, function(memo, text) {
        return memo + ' ' + text.text;
      }, '');
      text = self.apos.utils.sortify(text);
      return text;
    };

    // Invoked by the apostrophe-versions module.
    // Identify fields that should never be rolled back

    self.docUnversionedFields = function(req, doc, fields) {
      fields.push('titleSortified', 'highSearchText', 'highSearchWords', 'lowSearchText', 'searchSummary');
    };

  }
};
<|endoftext|>


<commit_before>/* global OC, $, _, t, Album, GalleryImage, SlideShow, Thumbnail, oc_requesttoken */
var Gallery = {};
Gallery.images = [];
Gallery.currentAlbum = '';
Gallery.users = [];
Gallery.albumMap = {};
Gallery.imageMap = {};
Gallery.appName = 'galleryplus';
Gallery.token = undefined;

Gallery.getAlbum = function (path, token) {
	if (!Gallery.albumMap[path]) {
		Gallery.albumMap[path] = new Album(path, [], [], OC.basename(path), token);
		if (path !== '') {
			var parent = OC.dirname(path);
			if (parent === path) {
				parent = '';
			}
			Gallery.getAlbum(parent, token).subAlbums.push(Gallery.albumMap[path]);
		}
	}
	return Gallery.albumMap[path];
};

// fill the albums from Gallery.images
Gallery.fillAlbums = function () {
	var sortFunction = function (a, b) {
		return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
	};
	var album, image;
	Gallery.images = [];
	Gallery.currentAlbum = '';
	Gallery.albumMap = {};
	Gallery.imageMap = {};
	var currentFolder = decodeURI(location.hash).substr(1);
	var url = Gallery.buildUrl('images', {currentfolder: currentFolder});
	return $.getJSON(url).then(function (data) {
		//Gallery.images = data;
		var path = null;
		var mimeType = null;
		for (var i = 0; i < data.length; i++) {
			path = data[i].path;
			Gallery.images.push(path);
			mimeType = data[i].mimetype;
			image = new GalleryImage(path, path, Gallery.token);
			image.mimeType = mimeType;
			var dir = OC.dirname(path);
			if (dir === path) {
				dir = '';
			}
			album = Gallery.getAlbum(dir, Gallery.token);
			album.images.push(image);
			Gallery.imageMap[image.path] = image;
		}

		for (path in Gallery.albumMap) {
			Gallery.albumMap[path].images.sort(sortFunction);
			Gallery.albumMap[path].subAlbums.sort(sortFunction);
		}
	});
};

Gallery.getAlbumInfo = function (album) {
	if (Gallery.token) {
		return [];
	}
	if (!Gallery.getAlbumInfo.cache[album]) {
		var def = new $.Deferred();
		Gallery.getAlbumInfo.cache[album] = def;

		var url = OC.generateUrl('apps/' + Gallery.appName +
		'/albums?albumpath={albumpath}', {albumpath: encodeURIComponent(album)});
		$.getJSON(url, function (data) {
			def.resolve(data);
		});
	}
	return Gallery.getAlbumInfo.cache[album];
};

Gallery.getAlbumInfo.cache = {};

Gallery.getImage = function (image) {
	var width = $(window).width() * window.devicePixelRatio;
	var height = $(window).height() * window.devicePixelRatio;
	var params = {
		file: image,
		x: width,
		y: height,
		requesttoken: oc_requesttoken
	};
	return Gallery.buildUrl('preview', params);
};

Gallery.share = function (event) {
	if (!OC.Share.droppedDown) {
		event.preventDefault();
		event.stopPropagation();

		(function () {
			var target = OC.Share.showLink;
			OC.Share.showLink = function () {
				var r = target.apply(this, arguments);
				$('#linkText').val($('#linkText').val().replace('index.php/s/', 'index.php/apps/' +
				Gallery.appName + '/s/'));

				return r;
			};
		})();

		Gallery.getAlbumInfo(Gallery.currentAlbum).then(function (info) {
			$('a.share').data('item', info.fileid).data('link', true)
				.data('possible-permissions', info.permissions).
				click();
			if (!$('#linkCheckbox').is(':checked')) {
				$('#linkText').hide();
			}
		});
	}
};

Gallery.buildUrl = function (endPoint, params) {
	var extension = '';
	if (Gallery.token) {
		params.token = Gallery.token;
		extension = '.public';
	}
	var query = OC.buildQueryString(params);
	return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension, null) + '?' +
		query;
};

Gallery.view = {};
Gallery.view.element = null;
Gallery.view.cache = {};

Gallery.view.clear = function () {
	Gallery.view.element.empty();
	Gallery.showLoading();
};

Gallery.view.viewAlbum = function (albumPath) {
	albumPath = albumPath || '';
	if (!Gallery.albumMap[albumPath]) {
		return;
	}

	Gallery.view.clear();
	if (albumPath !== Gallery.currentAlbum) {
		Gallery.view.loadVisibleRows.loading = false;
	}
	Gallery.currentAlbum = albumPath;
	Gallery.view.shareButton(albumPath);
	Gallery.view.buildBreadCrumb(albumPath);

	Gallery.getAlbumInfo(Gallery.currentAlbum); //preload album info
	Gallery.albumMap[albumPath].viewedItems = 0;
	setTimeout(function () {
		Gallery.view.loadVisibleRows.activeIndex = 0;
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	}, 0);
};

Gallery.view.shareButton = function (albumPath) {
	if (albumPath === '' || Gallery.token) {
		$('button.share').hide();
	} else {
		$('button.share').show();
	}
};

Gallery.view.loadVisibleRows = function (album, path) {
	if (Gallery.view.loadVisibleRows.loading &&
		Gallery.view.loadVisibleRows.loading.state() !== 'resolved') {
		return Gallery.view.loadVisibleRows.loading;
	}
	// load 2 windows worth of rows
	var scroll = $('#content-wrapper').scrollTop() + $(window).scrollTop();
	var targetHeight = ($(window).height() * 2) + scroll;
	var showRows = function (album) {
		if (!(album.viewedItems < album.subAlbums.length + album.images.length)) {
			Gallery.view.loadVisibleRows.loading = null;
			return;
		}
		return album.getNextRow($(window).width()).then(function (row) {
			return row.getDom().then(function (dom) {
				// defer removal of loading class to trigger CSS3 animation
				_.defer(function () {
					dom.removeClass('loading');
				});
				if (Gallery.currentAlbum !== path) {
					Gallery.view.loadVisibleRows.loading = null;
					return; //throw away the row if the user has navigated away in the meantime
				}
				if (Gallery.view.element.length === 1) {
					Gallery.showNormal();
				}
				Gallery.view.element.append(dom);
				if (album.viewedItems < album.subAlbums.length + album.images.length &&
					Gallery.view.element.height() < targetHeight) {
					return showRows(album);
				} else {
					Gallery.view.loadVisibleRows.loading = null;
				}
			}, function () {
				Gallery.view.loadVisibleRows.loading = null;
			});
		});
	};
	if (Gallery.view.element.height() < targetHeight) {
		Gallery.view.loadVisibleRows.loading = true;
		Gallery.view.loadVisibleRows.loading = showRows(album);
		return Gallery.view.loadVisibleRows.loading;
	}
};
Gallery.view.loadVisibleRows.loading = false;

Gallery.view.buildBreadCrumb = function (albumPath) {
	var i, crumbs, path;
	OC.Breadcrumb.clear();
	var albumName = $('#content').data('albumname');
	if (!albumName) {
		albumName = t('gallery', 'Pictures');
	}
	Gallery.view.pushBreadCrumb(albumName, '');

	path = '';
	crumbs = albumPath.split('/');
	for (i = 0; i < crumbs.length; i++) {
		if (crumbs[i]) {
			if (path) {
				path += '/' + crumbs[i];
			} else {
				path += crumbs[i];
			}
			Gallery.view.pushBreadCrumb(crumbs[i], path);
		}
	}
};

Gallery.view.pushBreadCrumb = function (text, path) {
	OC.Breadcrumb.push(text, '#' + path);
};

Gallery.hideSearch = function () {
	$('form.searchbox').hide();
};

Gallery.showEmpty = function () {
	$('#emptycontent').removeClass('hidden');
	$('#controls').addClass('hidden');
	$('#content').removeClass('icon-loading');
};

Gallery.showLoading = function () {
	$('#emptycontent').addClass('hidden');
	$('#controls').removeClass('hidden');
	$('#content').addClass('icon-loading');
};

Gallery.showNormal = function () {
	$('#emptycontent').addClass('hidden');
	$('#controls').removeClass('hidden');
	$('#content').removeClass('icon-loading');
};

Gallery.slideShow = function (images, startImage, autoPlay) {
	if (startImage === undefined) {
		OC.Notification.showTemporary(t('gallery', 'Aborting preview. Could not find the file'));
		return false;
	}
	var start = images.indexOf(startImage);
	images = images.map(function (image) {
		var name = OC.basename(image.path);
		var url = Gallery.getImage(image.src);
		var params = {
			file: image.src,
			requesttoken: oc_requesttoken
		};
		var downloadUrl = Gallery.buildUrl('download', params);

		return {
			name: name,
			path: image.path,
			mimeType: image.mimeType,
			url: url,
			downloadUrl: downloadUrl
		};
	});

	var slideShow = new SlideShow($('#slideshow'), images);
	Thumbnail.concurrent = 1;
	slideShow.onStop = function () {
		Gallery.activeSlideShow = null;
		$('#content').show();
		location.hash = encodeURI(Gallery.currentAlbum);
		Thumbnail.concurrent = 3;
	};
	Gallery.activeSlideShow = slideShow;

	slideShow.init(autoPlay);
	slideShow.show(start);
};

Gallery.activeSlideShow = null;

$(document).ready(function () {
	Gallery.hideSearch();
	Gallery.showLoading();

	Gallery.view.element = $('#gallery');
	if (Gallery.view.element.data('token')) {
		Gallery.token = Gallery.view.element.data('token');
	}

	if (Gallery.view.element.data('requesttoken')) {
		oc_requesttoken = Gallery.view.element.data('requesttoken');
	}

	Gallery.fillAlbums().then(function () {
		if (Gallery.images.length === 0) {
			Gallery.showEmpty();
		} else {
			OC.Breadcrumb.container = $('#breadcrumbs');
			window.onhashchange();
			$('button.share').click(Gallery.share);
		}
	});

	$('#openAsFileListButton').click(function () {
		var subUrl = '';
		var params = {path: '/' + Gallery.currentAlbum};
		if (Gallery.token) {
			params.token = Gallery.token;
			subUrl = 's/{token}?path={path}';
		} else {
			subUrl = 'apps/files?dir={path}';
		}
		window.location.href = OC.generateUrl(subUrl, params);
	});
	$('#download').click(function (e) {
		e.preventDefault();
		OC.redirect(OC.generateUrl('s/{token}/download?path={path}&files={files}', {
			token: Gallery.token,
			path: $('#content').data('albumname'),
			files: Gallery.currentAlbum
		}));
	});

	$(window).scroll(function () {
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	});
	$('#content-wrapper').scroll(function () {
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	});

	$(window).resize(_.throttle(function () {
		Gallery.view.viewAlbum(Gallery.currentAlbum);
	}, 500));
});

window.onhashchange = function () {
	var path = decodeURI(location.hash).substr(1);
	if (Gallery.albumMap[path]) {
		if (Gallery.activeSlideShow) {
			Gallery.activeSlideShow.stop();
		}
		path = decodeURIComponent(path);
		if (Gallery.currentAlbum !== path || path === '') {
			Gallery.fillAlbums().then(function () {
				Gallery.view.viewAlbum(path);
			});
		}
	} else if (!Gallery.activeSlideShow) {
		var albumPath = OC.dirname(path);
		if (albumPath === path) {
			albumPath = '';
		}
		if (Gallery.currentAlbum !== albumPath || albumPath === '') {
			Gallery.view.viewAlbum(albumPath);
		}
		var album = Gallery.albumMap[albumPath];
		var images = album.images;
		var startImage = Gallery.imageMap[path];
		Gallery.slideShow(images, startImage);
	}
};
<commit_msg>Don't duplicate queries<commit_after>/* global OC, $, _, t, Album, GalleryImage, SlideShow, Thumbnail, oc_requesttoken */
var Gallery = {};
Gallery.images = [];
Gallery.currentAlbum = '';
Gallery.users = [];
Gallery.albumMap = {};
Gallery.imageMap = {};
Gallery.appName = 'galleryplus';
Gallery.token = undefined;

Gallery.getAlbum = function (path, token) {
	if (!Gallery.albumMap[path]) {
		Gallery.albumMap[path] = new Album(path, [], [], OC.basename(path), token);
		if (path !== '') {
			var parent = OC.dirname(path);
			if (parent === path) {
				parent = '';
			}
			Gallery.getAlbum(parent, token).subAlbums.push(Gallery.albumMap[path]);
		}
	}
	return Gallery.albumMap[path];
};

// fill the albums from Gallery.images
Gallery.fillAlbums = function () {
	var sortFunction = function (a, b) {
		return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
	};
	var album, image;
	Gallery.images = [];
	Gallery.currentAlbum = '';
	Gallery.albumMap = {};
	Gallery.imageMap = {};
	var currentFolder = decodeURI(location.hash).substr(1);
	var url = Gallery.buildUrl('images', {currentfolder: currentFolder});
	return $.getJSON(url).then(function (data) {
		//Gallery.images = data;
		var path = null;
		var mimeType = null;
		for (var i = 0; i < data.length; i++) {
			path = data[i].path;
			Gallery.images.push(path);
			mimeType = data[i].mimetype;
			image = new GalleryImage(path, path, Gallery.token);
			image.mimeType = mimeType;
			var dir = OC.dirname(path);
			if (dir === path) {
				dir = '';
			}
			album = Gallery.getAlbum(dir, Gallery.token);
			album.images.push(image);
			Gallery.imageMap[image.path] = image;
		}

		for (path in Gallery.albumMap) {
			Gallery.albumMap[path].images.sort(sortFunction);
			Gallery.albumMap[path].subAlbums.sort(sortFunction);
		}
	});
};

Gallery.getAlbumInfo = function (album) {
	if (Gallery.token) {
		return [];
	}
	if (!Gallery.getAlbumInfo.cache[album]) {
		var def = new $.Deferred();
		Gallery.getAlbumInfo.cache[album] = def;

		var url = OC.generateUrl('apps/' + Gallery.appName +
		'/albums?albumpath={albumpath}', {albumpath: encodeURIComponent(album)});
		$.getJSON(url, function (data) {
			def.resolve(data);
		});
	}
	return Gallery.getAlbumInfo.cache[album];
};

Gallery.getAlbumInfo.cache = {};

Gallery.getImage = function (image) {
	var width = $(window).width() * window.devicePixelRatio;
	var height = $(window).height() * window.devicePixelRatio;
	var params = {
		file: image,
		x: width,
		y: height,
		requesttoken: oc_requesttoken
	};
	return Gallery.buildUrl('preview', params);
};

Gallery.share = function (event) {
	if (!OC.Share.droppedDown) {
		event.preventDefault();
		event.stopPropagation();

		(function () {
			var target = OC.Share.showLink;
			OC.Share.showLink = function () {
				var r = target.apply(this, arguments);
				$('#linkText').val($('#linkText').val().replace('index.php/s/', 'index.php/apps/' +
				Gallery.appName + '/s/'));

				return r;
			};
		})();

		Gallery.getAlbumInfo(Gallery.currentAlbum).then(function (info) {
			$('a.share').data('item', info.fileid).data('link', true)
				.data('possible-permissions', info.permissions).
				click();
			if (!$('#linkCheckbox').is(':checked')) {
				$('#linkText').hide();
			}
		});
	}
};

Gallery.buildUrl = function (endPoint, params) {
	var extension = '';
	if (Gallery.token) {
		params.token = Gallery.token;
		extension = '.public';
	}
	var query = OC.buildQueryString(params);
	return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension, null) + '?' +
		query;
};

Gallery.view = {};
Gallery.view.element = null;
Gallery.view.cache = {};

Gallery.view.clear = function () {
	Gallery.view.element.empty();
	Gallery.showLoading();
};

Gallery.view.viewAlbum = function (albumPath) {
	albumPath = albumPath || '';
	if (!Gallery.albumMap[albumPath]) {
		return;
	}

	Gallery.view.clear();
	if (albumPath !== Gallery.currentAlbum) {
		Gallery.view.loadVisibleRows.loading = false;
	}
	Gallery.currentAlbum = albumPath;
	Gallery.view.shareButton(albumPath);
	Gallery.view.buildBreadCrumb(albumPath);

	Gallery.getAlbumInfo(Gallery.currentAlbum); //preload album info
	Gallery.albumMap[albumPath].viewedItems = 0;
	setTimeout(function () {
		Gallery.view.loadVisibleRows.activeIndex = 0;
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	}, 0);
};

Gallery.view.shareButton = function (albumPath) {
	var shareButton = $('button.share');
	if (albumPath === '' || Gallery.token) {
		shareButton.hide();
	} else {
		shareButton.show();
	}
};

Gallery.view.loadVisibleRows = function (album, path) {
	if (Gallery.view.loadVisibleRows.loading &&
		Gallery.view.loadVisibleRows.loading.state() !== 'resolved') {
		return Gallery.view.loadVisibleRows.loading;
	}
	// load 2 windows worth of rows
	var scroll = $('#content-wrapper').scrollTop() + $(window).scrollTop();
	var targetHeight = ($(window).height() * 2) + scroll;
	var showRows = function (album) {
		if (!(album.viewedItems < album.subAlbums.length + album.images.length)) {
			Gallery.view.loadVisibleRows.loading = null;
			return;
		}
		return album.getNextRow($(window).width()).then(function (row) {
			return row.getDom().then(function (dom) {
				// defer removal of loading class to trigger CSS3 animation
				_.defer(function () {
					dom.removeClass('loading');
				});
				if (Gallery.currentAlbum !== path) {
					Gallery.view.loadVisibleRows.loading = null;
					return; //throw away the row if the user has navigated away in the meantime
				}
				if (Gallery.view.element.length === 1) {
					Gallery.showNormal();
				}
				Gallery.view.element.append(dom);
				if (album.viewedItems < album.subAlbums.length + album.images.length &&
					Gallery.view.element.height() < targetHeight) {
					return showRows(album);
				} else {
					Gallery.view.loadVisibleRows.loading = null;
				}
			}, function () {
				Gallery.view.loadVisibleRows.loading = null;
			});
		});
	};
	if (Gallery.view.element.height() < targetHeight) {
		Gallery.view.loadVisibleRows.loading = true;
		Gallery.view.loadVisibleRows.loading = showRows(album);
		return Gallery.view.loadVisibleRows.loading;
	}
};
Gallery.view.loadVisibleRows.loading = false;

Gallery.view.buildBreadCrumb = function (albumPath) {
	var i, crumbs, path;
	OC.Breadcrumb.clear();
	var albumName = $('#content').data('albumname');
	if (!albumName) {
		albumName = t('gallery', 'Pictures');
	}
	Gallery.view.pushBreadCrumb(albumName, '');

	path = '';
	crumbs = albumPath.split('/');
	for (i = 0; i < crumbs.length; i++) {
		if (crumbs[i]) {
			if (path) {
				path += '/' + crumbs[i];
			} else {
				path += crumbs[i];
			}
			Gallery.view.pushBreadCrumb(crumbs[i], path);
		}
	}
};

Gallery.view.pushBreadCrumb = function (text, path) {
	OC.Breadcrumb.push(text, '#' + path);
};

Gallery.hideSearch = function () {
	$('form.searchbox').hide();
};

Gallery.showEmpty = function () {
	$('#emptycontent').removeClass('hidden');
	$('#controls').addClass('hidden');
	$('#content').removeClass('icon-loading');
};

Gallery.showLoading = function () {
	$('#emptycontent').addClass('hidden');
	$('#controls').removeClass('hidden');
	$('#content').addClass('icon-loading');
};

Gallery.showNormal = function () {
	$('#emptycontent').addClass('hidden');
	$('#controls').removeClass('hidden');
	$('#content').removeClass('icon-loading');
};

Gallery.slideShow = function (images, startImage, autoPlay) {
	if (startImage === undefined) {
		OC.Notification.showTemporary(t('gallery', 'Aborting preview. Could not find the file'));
		return false;
	}
	var start = images.indexOf(startImage);
	images = images.map(function (image) {
		var name = OC.basename(image.path);
		var url = Gallery.getImage(image.src);
		var params = {
			file: image.src,
			requesttoken: oc_requesttoken
		};
		var downloadUrl = Gallery.buildUrl('download', params);

		return {
			name: name,
			path: image.path,
			mimeType: image.mimeType,
			url: url,
			downloadUrl: downloadUrl
		};
	});

	var slideShow = new SlideShow($('#slideshow'), images);
	Thumbnail.concurrent = 1;
	slideShow.onStop = function () {
		Gallery.activeSlideShow = null;
		$('#content').show();
		location.hash = encodeURI(Gallery.currentAlbum);
		Thumbnail.concurrent = 3;
	};
	Gallery.activeSlideShow = slideShow;

	slideShow.init(autoPlay);
	slideShow.show(start);
};

Gallery.activeSlideShow = null;

$(document).ready(function () {
	Gallery.hideSearch();
	Gallery.showLoading();

	Gallery.view.element = $('#gallery');
	if (Gallery.view.element.data('token')) {
		Gallery.token = Gallery.view.element.data('token');
	}

	if (Gallery.view.element.data('requesttoken')) {
		oc_requesttoken = Gallery.view.element.data('requesttoken');
	}

	Gallery.fillAlbums().then(function () {
		if (Gallery.images.length === 0) {
			Gallery.showEmpty();
		} else {
			OC.Breadcrumb.container = $('#breadcrumbs');
			window.onhashchange();
			$('button.share').click(Gallery.share);
		}
	});

	$('#openAsFileListButton').click(function () {
		var subUrl = '';
		var params = {path: '/' + Gallery.currentAlbum};
		if (Gallery.token) {
			params.token = Gallery.token;
			subUrl = 's/{token}?path={path}';
		} else {
			subUrl = 'apps/files?dir={path}';
		}
		window.location.href = OC.generateUrl(subUrl, params);
	});
	$('#download').click(function (e) {
		e.preventDefault();
		OC.redirect(OC.generateUrl('s/{token}/download?path={path}&files={files}', {
			token: Gallery.token,
			path: $('#content').data('albumname'),
			files: Gallery.currentAlbum
		}));
	});

	$(window).scroll(function () {
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	});
	$('#content-wrapper').scroll(function () {
		Gallery.view.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum], Gallery.currentAlbum);
	});

	$(window).resize(_.throttle(function () {
		Gallery.view.viewAlbum(Gallery.currentAlbum);
	}, 500));
});

window.onhashchange = function () {
	var path = decodeURI(location.hash).substr(1);
	if (Gallery.albumMap[path]) {
		if (Gallery.activeSlideShow) {
			Gallery.activeSlideShow.stop();
		}
		path = decodeURIComponent(path);
		if (Gallery.currentAlbum !== path || path === '') {
			Gallery.fillAlbums().then(function () {
				Gallery.view.viewAlbum(path);
			});
		}
	} else if (!Gallery.activeSlideShow) {
		var albumPath = OC.dirname(path);
		if (albumPath === path) {
			albumPath = '';
		}
		if (Gallery.currentAlbum !== albumPath || albumPath === '') {
			Gallery.view.viewAlbum(albumPath);
		}
		var album = Gallery.albumMap[albumPath];
		var images = album.images;
		var startImage = Gallery.imageMap[path];
		Gallery.slideShow(images, startImage);
	}
};
<|endoftext|>