<commit_before>var torrents = require('torrent-stream')
var request = require('request')
var duplex = require('duplexify')
var ytdl = require('ytdl-core')
var events = require('events')
var path = require('path')
var fs = require('fs')
var vtt = require('srt-to-vtt')
var concat = require('concat-stream')

var noop = function () {}

module.exports = function () {
  var that = new events.EventEmitter()

  that.entries = []

  var onmagnet = function (link, cb) {
    console.log('torrent ' + link)

    var engine = torrents(link)

    engine.swarm.add('127.0.0.1:51413') // me!

    engine.on('ready', function () {
      console.log('torrent ready')

      engine.files.forEach(function (f) {
        f.downloadSpeed = engine.swarm.downloadSpeed
        if (/\.(mp4|mkv|mp3)$/i.test(f.name)) {
          f.select()
          f.id = that.entries.push(f) - 1
        }
      })

      setInterval(function () {
        console.log(engine.swarm.downloadSpeed() + ' (' + engine.swarm.wires.length + ')')
      }, 1000)

      that.emit('update')
      cb()
    })
  }

  var ontorrent = function (link, cb) {
    fs.readFile(link, function (err, buf) {
      if (err) return cb(err)
      onmagnet(buf, cb)
    })
  }

  var onyoutube = function (link, cb) {
    var file = {}
    var url = /https?:/.test(link) ? link : 'https:' + link

    getYoutubeData(function (err, data) {
      if (err) return cb(err)
      var fmt = data.fmt
      var info = data.info
      request({method: 'HEAD', url: fmt.url}, function (err, resp, body) {
        if (err) return cb(err)
        var len = resp.headers['content-length']
        if (!len) return cb(new Error('no content-length on response'))
        file.length = +len
        file.name = info.title

        file.createReadStream = function (opts) {
          if (!opts) opts = {}
          // fetch this for every range request
          // TODO try and avoid doing this call twice the first time
          getYoutubeData(function (err, data) {
            if (err) return cb(err)
            var vidUrl = data.fmt.url
            if (opts.start || opts.end) vidUrl += '&range=' + ([opts.start || 0, opts.end || len].join('-'))
            stream.setReadable(request(vidUrl))
          })

          var stream = duplex()
          return stream
        }
        file.id = that.entries.push(file) - 1
        that.emit('update')
        cb()
      })
    })

    function getYoutubeData (cb) {
      ytdl.getInfo(url, function (err, info) {
        if (err) return cb(err)

        var vidFmt
        var formats = info.formats

        formats.sort(function sort (a, b) {
          return +a.itag - +b.itag
        })

        var vidFmt
        formats.forEach(function (fmt) {
          // prefer webm
          if (fmt.itag === '46') return vidFmt = fmt
          if (fmt.itag === '45') return vidFmt = fmt
          if (fmt.itag === '44') return vidFmt = fmt
          if (fmt.itag === '43') return vidFmt = fmt

          // otherwise h264
          if (fmt.itag === '38') return vidFmt = fmt
          if (fmt.itag === '37') return vidFmt = fmt
          if (fmt.itag === '22') return vidFmt = fmt
          if (fmt.itag === '18') return vidFmt = fmt
        })

        if (!vidFmt) return cb (new Error('No suitable video format found'))

        cb(null, {info: info, fmt: vidFmt})
      })
    }
  }

  var onfile = function (link, cb) {
    var file = {}

    fs.stat(link, function (err, st) {
      if (err) return cb(err)

      file.length = st.size
      file.name = path.basename(link)
      file.createReadStream = function (opts) {
        return fs.createReadStream(link, opts)
      }

      file.id = that.entries.push(file) - 1

      var ondone = function () {
        that.emit('update')
        cb()
      }
      var basename = link.substr(0, link.lastIndexOf('.'))
      var extensions = ['srt', 'vtt']
      var next = function () {
        var ext = extensions.shift()
        if (!ext) return ondone()

        fs.exists(basename + '.' + ext, function(exists) {
          if (!exists) return next()
          fs.createReadStream(basename + '.' + ext).pipe(vtt()).pipe(concat(function(data) {
            file.subtitles = data
            ondone()
          }))
        })
      }
      next()
    })
  }

  var onhttplink = function (link, cb) {
    var file = {}

    file.name = link.lastIndexOf('/') > -1 ? link.split('/').pop() : link

    file.createReadStream = function (opts) {
      if (!opts) opts = {}

      if (opts && (opts.start || opts.end)) {
        var rs = 'bytes=' + (opts.start || 0) + '-' + (opts.end || file.length || '')
        return request(link, {headers: {Range: rs}})
      }

      return request(link)
    }

    // first, get the head for the content length.
    // IMPORTANT: servers without HEAD will not work.
    request.head(link, function (err, response) {
      if (err) return cb(err)
      if (!/2\d\d/.test(response.statusCode)) return cb(new Error('request failed'))

      file.length = Number(response.headers['content-length'])
      file.id = that.entries.push(file) - 1
      that.emit('update')
      cb()
    })
  }

  var onipfslink = function (link, cb) {
    if (link[0] != '/') link = "/" + link // / may be stripped in add

    var local = 'localhost:8080' // todo: make this configurable
    var gateway = 'gateway.ipfs.io'
    var file = {}

    // first, try the local http gateway
    var u = 'http://' + local + link
    console.log('trying local ipfs gateway: ' + u)
    onhttplink(u, function (err) {
      if (!err) return cb() // done.

      // error? ok try fuse... maybe the gateway's broken.
      console.log('trying mounted ipfs fs (just in case)')
      onfile(link, function (err) {
        if (!err) return cb() // done.

        // worst case, try global ipfs gateway.
        var u = 'http://' + gateway + link
        console.log('trying local ipfs gateway: ' + u)
        onhttplink(u, cb)
      })
    })
  }

  that.selected = null

  that.deselect = function () {
    that.selected = null
    that.emit('deselect')
  }

  that.selectNext = function () {
    if (!that.entries.length) return null
    if (!that.selected) return that.select(0)
    if (that.selected.id === that.entries.length - 1) return null
    return that.select(that.selected.id + 1)
  }

  that.select = function (id) {
    that.selected = that.get(id)
    that.emit('select')
    return that.selected
  }

  that.get = function (id) {
    return that.entries[id]
  }

  that.add = function (link, cb) {
    link = link.replace('playback://', '').replace('playback:', '') // strip playback protocol
    if (!cb) cb = noop
    if (/magnet:/.test(link)) return onmagnet(link, cb)
    if (/\.torrent$/i.test(link)) return ontorrent(link, cb)
    if (/youtube\.com\/watch/i.test(link)) return onyoutube(link, cb)
    if (/^\/*(ipfs|ipns)\//i.test(link)) return onipfslink(link, cb)
    if (/^\/https?:\/\//i.test(link)) return onhttplink(link, cb)
    onfile(link, cb)
  }

  return that
}<commit_msg>Use available subtitles while streaming torrents<commit_after>var torrents = require('torrent-stream')
var request = require('request')
var duplex = require('duplexify')
var ytdl = require('ytdl-core')
var events = require('events')
var path = require('path')
var fs = require('fs')
var vtt = require('srt-to-vtt')
var concat = require('concat-stream')

var noop = function () {}

module.exports = function () {
  var that = new events.EventEmitter()

  that.entries = []

  var onmagnet = function (link, cb) {
    console.log('torrent ' + link)

    var engine = torrents(link)
    var subtitles = {}

    engine.swarm.add('127.0.0.1:51413') // me!

    engine.on('ready', function () {
      console.log('torrent ready')

      engine.files.forEach(function (f) {
        if (/\.(vtt|srt)$/i.test(f.name)) {
          subtitles[f.name] = f;
        }
      })

      engine.files.forEach(function (f) {
        f.downloadSpeed = engine.swarm.downloadSpeed
        if (/\.(mp4|mkv|mp3)$/i.test(f.name)) {
          f.select()
          f.id = that.entries.push(f) - 1

          var basename = f.name.substr(0, f.name.lastIndexOf('.'))
          var subtitle = subtitles[basename + '.srt'] || subtitles[basename + '.vtt']
          if (subtitle) {
             subtitle.createReadStream().pipe(vtt()).pipe(concat(function(data) {
               f.subtitles = data
            }))
          }
        }

      })

      setInterval(function () {
        console.log(engine.swarm.downloadSpeed() + ' (' + engine.swarm.wires.length + ')')
      }, 1000)

      that.emit('update')
      cb()
    })
  }

  var ontorrent = function (link, cb) {
    fs.readFile(link, function (err, buf) {
      if (err) return cb(err)
      onmagnet(buf, cb)
    })
  }

  var onyoutube = function (link, cb) {
    var file = {}
    var url = /https?:/.test(link) ? link : 'https:' + link

    getYoutubeData(function (err, data) {
      if (err) return cb(err)
      var fmt = data.fmt
      var info = data.info
      request({method: 'HEAD', url: fmt.url}, function (err, resp, body) {
        if (err) return cb(err)
        var len = resp.headers['content-length']
        if (!len) return cb(new Error('no content-length on response'))
        file.length = +len
        file.name = info.title

        file.createReadStream = function (opts) {
          if (!opts) opts = {}
          // fetch this for every range request
          // TODO try and avoid doing this call twice the first time
          getYoutubeData(function (err, data) {
            if (err) return cb(err)
            var vidUrl = data.fmt.url
            if (opts.start || opts.end) vidUrl += '&range=' + ([opts.start || 0, opts.end || len].join('-'))
            stream.setReadable(request(vidUrl))
          })

          var stream = duplex()
          return stream
        }
        file.id = that.entries.push(file) - 1
        that.emit('update')
        cb()
      })
    })

    function getYoutubeData (cb) {
      ytdl.getInfo(url, function (err, info) {
        if (err) return cb(err)

        var vidFmt
        var formats = info.formats

        formats.sort(function sort (a, b) {
          return +a.itag - +b.itag
        })

        var vidFmt
        formats.forEach(function (fmt) {
          // prefer webm
          if (fmt.itag === '46') return vidFmt = fmt
          if (fmt.itag === '45') return vidFmt = fmt
          if (fmt.itag === '44') return vidFmt = fmt
          if (fmt.itag === '43') return vidFmt = fmt

          // otherwise h264
          if (fmt.itag === '38') return vidFmt = fmt
          if (fmt.itag === '37') return vidFmt = fmt
          if (fmt.itag === '22') return vidFmt = fmt
          if (fmt.itag === '18') return vidFmt = fmt
        })

        if (!vidFmt) return cb (new Error('No suitable video format found'))

        cb(null, {info: info, fmt: vidFmt})
      })
    }
  }

  var onfile = function (link, cb) {
    var file = {}

    fs.stat(link, function (err, st) {
      if (err) return cb(err)

      file.length = st.size
      file.name = path.basename(link)
      file.createReadStream = function (opts) {
        return fs.createReadStream(link, opts)
      }

      file.id = that.entries.push(file) - 1

      var ondone = function () {
        that.emit('update')
        cb()
      }
      var basename = link.substr(0, link.lastIndexOf('.'))
      var extensions = ['srt', 'vtt']
      var next = function () {
        var ext = extensions.shift()
        if (!ext) return ondone()

        fs.exists(basename + '.' + ext, function(exists) {
          if (!exists) return next()
          fs.createReadStream(basename + '.' + ext).pipe(vtt()).pipe(concat(function(data) {
            file.subtitles = data
            ondone()
          }))
        })
      }
      next()
    })
  }

  var onhttplink = function (link, cb) {
    var file = {}

    file.name = link.lastIndexOf('/') > -1 ? link.split('/').pop() : link

    file.createReadStream = function (opts) {
      if (!opts) opts = {}

      if (opts && (opts.start || opts.end)) {
        var rs = 'bytes=' + (opts.start || 0) + '-' + (opts.end || file.length || '')
        return request(link, {headers: {Range: rs}})
      }

      return request(link)
    }

    // first, get the head for the content length.
    // IMPORTANT: servers without HEAD will not work.
    request.head(link, function (err, response) {
      if (err) return cb(err)
      if (!/2\d\d/.test(response.statusCode)) return cb(new Error('request failed'))

      file.length = Number(response.headers['content-length'])
      file.id = that.entries.push(file) - 1
      that.emit('update')
      cb()
    })
  }

  var onipfslink = function (link, cb) {
    if (link[0] != '/') link = "/" + link // / may be stripped in add

    var local = 'localhost:8080' // todo: make this configurable
    var gateway = 'gateway.ipfs.io'
    var file = {}

    // first, try the local http gateway
    var u = 'http://' + local + link
    console.log('trying local ipfs gateway: ' + u)
    onhttplink(u, function (err) {
      if (!err) return cb() // done.

      // error? ok try fuse... maybe the gateway's broken.
      console.log('trying mounted ipfs fs (just in case)')
      onfile(link, function (err) {
        if (!err) return cb() // done.

        // worst case, try global ipfs gateway.
        var u = 'http://' + gateway + link
        console.log('trying local ipfs gateway: ' + u)
        onhttplink(u, cb)
      })
    })
  }

  that.selected = null

  that.deselect = function () {
    that.selected = null
    that.emit('deselect')
  }

  that.selectNext = function () {
    if (!that.entries.length) return null
    if (!that.selected) return that.select(0)
    if (that.selected.id === that.entries.length - 1) return null
    return that.select(that.selected.id + 1)
  }

  that.select = function (id) {
    that.selected = that.get(id)
    that.emit('select')
    return that.selected
  }

  that.get = function (id) {
    return that.entries[id]
  }

  that.add = function (link, cb) {
    link = link.replace('playback://', '').replace('playback:', '') // strip playback protocol
    if (!cb) cb = noop
    if (/magnet:/.test(link)) return onmagnet(link, cb)
    if (/\.torrent$/i.test(link)) return ontorrent(link, cb)
    if (/youtube\.com\/watch/i.test(link)) return onyoutube(link, cb)
    if (/^\/*(ipfs|ipns)\//i.test(link)) return onipfslink(link, cb)
    if (/^\/https?:\/\//i.test(link)) return onhttplink(link, cb)
    onfile(link, cb)
  }

  return that
}
<|endoftext|>


<commit_before>$AffixController.$inject = ['$scope', '$element', '$attrs', '$transclude', '$location', '$anchorScroll'];
function $AffixController ($scope, $element, $attrs, $transclude, $location, $anchorScroll) {
	var ctrl = this;
	var items = this.items = {};

	this.activeItem = {};

	this.activate = function (href) {
		ctrl.activeItem = ctrl.items[href];
	};

	this.updateHash = function () {
		$location.hash(this.activeItem.link);
	};

	this.go = function (href) {
		if(this.activeItem.link !== href) {
			this.activate(href);
			this.updateHash();
		}
	};

	this.set = function (href, obj) {
		this.items[href] = obj;
	};

	this.setDisabled = function (href) {
		this.activeItem = {};
	};

	this.isActive = function (href) {
		return this.activeItem === this.items[href];
	};

	$scope.$watch(function () {
		return ctrl.activeItem.link;
	}, function (href, oldHref) {
		if(href === oldHref) {
			return;
		}

		if(_.isString(href)) {
			$scope.$emit('$affixChanged', ctrl.activeItem);
		}
	});
}

function AffixDirective () {
	return {
		controller: $AffixController,
		controllerAs: 'mbAffixCtrl'
	};
}

AffixHrefDirective.$inject = ['$animate'];
function AffixHrefDirective ($animate) {
	return {
		scope: {
			href: '@mbAffixHref'
		},
		require: '?^mbAffix',
		link: function (scope, element, attrs, mbAffix) {
			if(!mbAffix) {
				return;
			}

			element.on('click', function () {
				scope.$apply(function () {
					mbAffix.go(scope.href);
				});
			});

			scope.$watch(function () {
				return mbAffix.activeItem.link;
			}, function (href, oldHref) {
				if(href === oldHref) {
					return;
				}

				var isActive = (href === scope.href);

				$animate[isActive ? 'addClass' : 'removeClass'](element.parent('li'), 'mb-affix-active');
			});
		}
	};
}

AffixContentDirective.$inject = ['$window', '$animate'];
function AffixContentDirective ($window, $animate) {
	$window = angular.element($window);

	function preLink (scope, element, attrs, mbAffix) {
		if(!mbAffix) {
			return;
		}

		if(_.isUndefined(scope.example)) {
			scope.example = true;
		}

		mbAffix.set(scope.href, {
			isActive: false,
			link: scope.href,
			example: scope.example
		});
	}

	function postLink (scope, element, attrs, mbAffix) {
		if(!mbAffix) {
			return;
		}

		function onScroll () {
			var isActive = false;

			var offset = element.offset();
			var pageYOffset = window.pageYOffset;

			// Out of range
			if(pageYOffset > offset.top + element.height()) {
				return;
			}

			if(pageYOffset < element.prop('offsetTop')) {
				isActive = false;
			} else {
				isActive = true;
			}

			if(mbAffix.isActive(scope.href) === isActive) {
				return;
			}

			scope.$apply(function () {
				mbAffix[isActive ? 'activate' : 'setDisabled'](scope.href);
			});
		}

		scope.$watch(function () {
			return mbAffix.isActive(scope.href);
		}, function (isActive) {
			$animate[isActive ? 'addClass' : 'removeClass'](element, 'mb-affix-active');
		});

		$window.on('scroll', onScroll);

		scope.$on('$destroy', function () {
			$window.off('scroll', onScroll);
		});
	}

	return {
		require: '?^mbAffix',
		scope: {
			href: '=mbAffixId',
			example: '=mbHasExample'
		},
		compile: function () {
			return {
				pre: preLink,
				post: postLink
			};
		}
	};
}

function AffixColumnDirective () {
	return function (scope, element, attrs) {
		window.addEventListener('scroll', function () {
			var boundingClientRect = element[0].getBoundingClientRect();
			var olderWidth = boundingClientRect.width;

			if(boundingClientRect.top <= window.scrollY) {
				element[0].style.position = 'fixed';
				element[0].style.top = '20px';
				element[0].style.width = olderWidth + 'px';
				element.addClass('mb-affix-column-fixed');
			} else {
				element[0].style.position = '';
				element[0].style.width = '';
				element.removeClass('mb-affix-column-fixed');
			}
		});
	}
}

AffixMobileTemplateDirective.$inject = ['$animate'];
function AffixMobileTemplateDirective ($animate) {
	return {
		require: '?^mbAffix',
		template: '
' +
			'' +
		'
',
		link: function (scope, element, attrs, mbAffix) {
			if(!mbAffix) {
				return;
			}

			scope._$items$_ = {};

			scope.$watchCollection(function () {
				return mbAffix.items;
			}, function (items) {
				scope._$items$_ = _(items).map(function (item) {
					var itemName = String(item.link).toLowerCase();
					var mobileTemplatePath = [itemName, 'mobile', 'html'].join('.');
					item.mobileTemplatePath = path.join('examples', mobileTemplatePath);

					return item;
				}).value()
			});

			var itemEl;
			scope.$watch('template', function (itemLink) {
				if(itemEl) {
					$animate.removeClass(itemEl, 'mb-affix-active');
				}

				itemEl = element[0].querySelector('#' + itemLink);

				if(!itemEl) {
					return;
				}

				$animate.addClass(itemEl, 'mb-affix-active');
			});

			scope.$on('$affixChanged', function (event, item) {
				if(item.example) {
					$animate.setClass(element, 'mb-visible', 'mb-hidden');
					scope.template = item.link;
				} else {
					$animate.setClass(element, 'mb-hidden', 'mb-visible');
				}
			});
		}
	};
}

angular.module('docsApp.affix', [])
.directive('mbAffixHref', AffixHrefDirective)
.directive('mbAffixColumn', AffixColumnDirective)
.directive('mbAffix', AffixDirective)
.directive('mbAffixContent', AffixContentDirective)
.directive('mbAffixMobileTemplate', AffixMobileTemplateDirective)<commit_msg>documentation/affix: now affix has a goToHash listener in each first header element<commit_after>$AffixController.$inject = ['$scope', '$element', '$attrs', '$transclude', '$location', '$anchorScroll'];
function $AffixController ($scope, $element, $attrs, $transclude, $location, $anchorScroll) {
	var ctrl = this;
	var items = this.items = {};

	this.activeItem = {};

	this.activate = function (href) {
		ctrl.activeItem = ctrl.items[href];
	};

	this.updateHash = function () {
		$location.hash(this.activeItem.link);
	};

	this.go = function (href) {
		if(this.activeItem.link !== href) {
			this.activate(href);
			this.updateHash();
		}
	};

	this.set = function (href, obj) {
		this.items[href] = obj;
	};

	this.setDisabled = function (href) {
		this.activeItem = {};
	};

	this.isActive = function (href) {
		return this.activeItem === this.items[href];
	};

	$scope.$watch(function () {
		return ctrl.activeItem.link;
	}, function (href, oldHref) {
		if(href === oldHref) {
			return;
		}

		if(_.isString(href)) {
			$scope.$emit('$affixChanged', ctrl.activeItem);
		}
	});
}

function AffixDirective () {
	return {
		controller: $AffixController,
		controllerAs: 'mbAffixCtrl'
	};
}

AffixHrefDirective.$inject = ['$animate'];
function AffixHrefDirective ($animate) {
	return {
		scope: {
			href: '@mbAffixHref'
		},
		require: '?^mbAffix',
		link: function (scope, element, attrs, mbAffix) {
			if(!mbAffix) {
				return;
			}

			element.on('click', function () {
				scope.$apply(function () {
					mbAffix.go(scope.href);
				});
			});

			scope.$watch(function () {
				return mbAffix.activeItem.link;
			}, function (href, oldHref) {
				if(href === oldHref) {
					return;
				}

				var isActive = (href === scope.href);

				$animate[isActive ? 'addClass' : 'removeClass'](element.parent('li'), 'mb-affix-active');
			});
		}
	};
}

AffixContentDirective.$inject = ['$window', '$animate'];
function AffixContentDirective ($window, $animate) {
	$window = angular.element($window);

	function preLink (scope, element, attrs, mbAffix) {
		if(!mbAffix) {
			return;
		}

		if(_.isUndefined(scope.example)) {
			scope.example = true;
		}

		mbAffix.set(scope.href, {
			isActive: false,
			link: scope.href,
			example: scope.example
		});
	}

	function postLink (scope, element, attrs, mbAffix) {
		if(!mbAffix) {
			return;
		}

		function onScroll () {
			var isActive = false;

			var offset = element.offset();
			var pageYOffset = window.pageYOffset;

			// Out of range
			if(pageYOffset > offset.top + element.height()) {
				return;
			}

			if(pageYOffset < element.prop('offsetTop')) {
				isActive = false;
			} else {
				isActive = true;
			}

			if(mbAffix.isActive(scope.href) === isActive) {
				return;
			}

			scope.$apply(function () {
				mbAffix[isActive ? 'activate' : 'setDisabled'](scope.href);
			});
		}

		scope.$watch(function () {
			return mbAffix.isActive(scope.href);
		}, function (isActive) {
			$animate[isActive ? 'addClass' : 'removeClass'](element, 'mb-affix-active');
		});

		$window.on('scroll', onScroll);

		scope.$on('$destroy', function () {
			$window.off('scroll', onScroll);
		});

		// Put the link in the first heading of the element content
		var firstHeadingEl = angular.element(element[0].querySelector('h1,h2,h3,h4,h5,h6'));
		firstHeadingEl.attr('id', scope.href);

		var anchorEl = angular.element('');
		anchorEl.on('click', function () {
			scope.$apply(function () {
				mbAffix.go(scope.href);
			});
		});

		var headingText = firstHeadingEl.text();
		anchorEl.text(headingText);

		firstHeadingEl.text('');
		firstHeadingEl.prepend(anchorEl);
	}

	return {
		require: '?^mbAffix',
		scope: {
			href: '=mbAffixId',
			example: '=mbHasExample'
		},
		compile: function () {
			return {
				pre: preLink,
				post: postLink
			};
		}
	};
}

function AffixColumnDirective () {
	return function (scope, element, attrs) {
		window.addEventListener('scroll', function () {
			var boundingClientRect = element[0].getBoundingClientRect();
			var olderWidth = boundingClientRect.width;

			if(boundingClientRect.top <= window.scrollY) {
				element[0].style.position = 'fixed';
				element[0].style.top = '20px';
				element[0].style.width = olderWidth + 'px';
				element.addClass('mb-affix-column-fixed');
			} else {
				element[0].style.position = '';
				element[0].style.width = '';
				element.removeClass('mb-affix-column-fixed');
			}
		});
	}
}

AffixMobileTemplateDirective.$inject = ['$animate'];
function AffixMobileTemplateDirective ($animate) {
	return {
		require: '?^mbAffix',
		template: '
' +
			'' +
		'
',
		link: function (scope, element, attrs, mbAffix) {
			if(!mbAffix) {
				return;
			}

			scope._$items$_ = {};

			scope.$watchCollection(function () {
				return mbAffix.items;
			}, function (items) {
				scope._$items$_ = _(items).map(function (item) {
					var itemName = String(item.link).toLowerCase();
					var mobileTemplatePath = [itemName, 'mobile', 'html'].join('.');
					item.mobileTemplatePath = path.join('examples', mobileTemplatePath);

					return item;
				}).value()
			});

			var itemEl;
			scope.$watch('template', function (itemLink) {
				if(itemEl) {
					$animate.removeClass(itemEl, 'mb-affix-active');
				}

				itemEl = element[0].querySelector('#' + itemLink);

				if(!itemEl) {
					return;
				}

				$animate.addClass(itemEl, 'mb-affix-active');
			});

			scope.$on('$affixChanged', function (event, item) {
				if(item.example) {
					$animate.setClass(element, 'mb-visible', 'mb-hidden');
					scope.template = item.link;
				} else {
					$animate.setClass(element, 'mb-hidden', 'mb-visible');
				}
			});
		}
	};
}

angular.module('docsApp.affix', [])
.directive('mbAffixHref', AffixHrefDirective)
.directive('mbAffixColumn', AffixColumnDirective)
.directive('mbAffix', AffixDirective)
.directive('mbAffixContent', AffixContentDirective)
.directive('mbAffixMobileTemplate', AffixMobileTemplateDirective)<|endoftext|>


<commit_before>/* global describe, it, xit, before, beforeEach, after, afterEach */
/* jslint node: true, esnext: true */

'use strict';

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should(),
  fs = require('fs'),
  path = require('path'),
  WebSocket = require('ws'),
  service = require('kronos-service'),
  ServiceKOA = require('../service').Service,
  ServiceProviderMixin = service.ServiceProviderMixin;

class ServiceProvider extends service.ServiceProviderMixin(service.Service) {}

const sp = new ServiceProvider();

describe('service-koa socket', function () {

  //this.timeout(200000);

  const ks1 = new ServiceKOA({
    name: 'my-name1',
    hostname: 'localhost',
    port: 1235
  }, sp);

  const se = ks1.createSocketEndpoint('test', '/test');

  it('endpooint is socket' () => assert.isTrue(se.socket));

  se.receive = message => {
    console.log(`se: ${message}`);
  };

  const socketUrl = 'ws://localhost:1235/test';

  it('socket', done => {
    ks1.configure({}).then(() => ks1.start().then(() => {

      ks1.koa.use(ctx => {
        ctx.type = 'text/html';
        ctx.body = fs.createReadStream(path.join(__dirname, 'fixtures', 'index.html'));
      });

      let ws = new WebSocket(socketUrl, {});

      ws.on('open', function open() {
        console.log('connected');
        ws.send(Date.now().toString(), {
          mask: true
        });
      });

      ws.on('close', () => {
        console.log('disconnected');
      });

      ws.on('message', (data, flags) => {
        console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);

        setTimeout(() => {
          ws.send(Date.now().toString(), {
            mask: true
          });
        }, 500);

        done();
      });

      //assert.equal(ks1.state, 'running');
    }));
  });
});
<commit_msg>test: fix typo<commit_after>/* global describe, it, xit, before, beforeEach, after, afterEach */
/* jslint node: true, esnext: true */

'use strict';

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should(),
  fs = require('fs'),
  path = require('path'),
  WebSocket = require('ws'),
  service = require('kronos-service'),
  ServiceKOA = require('../service').Service,
  ServiceProviderMixin = service.ServiceProviderMixin;

class ServiceProvider extends service.ServiceProviderMixin(service.Service) {}

const sp = new ServiceProvider();

describe('service-koa socket', function () {

  //this.timeout(200000);

  const ks1 = new ServiceKOA({
    name: 'my-name1',
    hostname: 'localhost',
    port: 1235
  }, sp);

  const se = ks1.createSocketEndpoint('test', '/test');

  it('endpooint is socket', () => assert.isTrue(se.socket));

  se.receive = message => {
    console.log(`se: ${message}`);
  };

  const socketUrl = 'ws://localhost:1235/test';

  it('socket', done => {
    ks1.configure({}).then(() => ks1.start().then(() => {

      ks1.koa.use(ctx => {
        ctx.type = 'text/html';
        ctx.body = fs.createReadStream(path.join(__dirname, 'fixtures', 'index.html'));
      });

      let ws = new WebSocket(socketUrl, {});

      ws.on('open', function open() {
        console.log('connected');
        ws.send(Date.now().toString(), {
          mask: true
        });
      });

      ws.on('close', () => {
        console.log('disconnected');
      });

      ws.on('message', (data, flags) => {
        console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);

        setTimeout(() => {
          ws.send(Date.now().toString(), {
            mask: true
          });
        }, 500);

        done();
      });

      //assert.equal(ks1.state, 'running');
    }));
  });
});
<|endoftext|>


<commit_before>/** @jsx React.DOM */
'use strict';
var React = require('react');
var globals = require('./globals');

var SignIn = module.exports.SignIn = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        var disabled = !this.props.personaReady;
        return (

Data Providers

                Sign In

No access? Request an account.

Authentication by Mozilla Persona.

        );
    }
});

var Home = module.exports.Home = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        return (

ENCODE: The Encyclopedia of DNA Elements

Preview the new ENCODE Portal.

Enter a search term like "skin", "ChIP-seq", or "CTCF" or select a data type from the toolbar.

        );
    }
});

globals.content_views.register(Home, 'portal');
<commit_msg>Updated wording and font.<commit_after>/** @jsx React.DOM */
'use strict';
var React = require('react');
var globals = require('./globals');

var SignIn = module.exports.SignIn = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        var disabled = !this.props.personaReady;
        return (

Data Providers

                Sign In

No access? Request an account.

Authentication by Mozilla Persona.

        );
    }
});

var Home = module.exports.Home = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        return (

ENCODE: The Encyclopedia of DNA Elements

Preview the new ENCODE Portal

Enter a search term like "skin", "ChIP-seq", or "CTCF" or select a data type in the toolbar above.

        );
    }
});

globals.content_views.register(Home, 'portal');
<|endoftext|>


<commit_before>import React from 'react';
import moment from 'moment';
import _ from 'underscore';
import url from 'url';
import globals from './globals';
import { Panel, PanelBody } from '../libs/bootstrap/panel';

// Display a news preview item from a search result @graph object.
const NewsPreviewItem = React.createClass({
    propTypes: {
        item: React.PropTypes.object, // News search result object to display
    },

    render: function () {
        const { item } = this.props;
        return (

{item.title}

{moment.utc(item.date_created).format('MMMM D, YYYY')}

{item.news_excerpt}

                    <a className="btn btn-info btn-sm" href={item['@id']} title={`View news post for ${item.title}`} key={item['@id']}>Read more

        );
    },
});

// Display a list of news preview items from search results.
const NewsPreviews = React.createClass({
    propTypes: {
        items: React.PropTypes.array, // Items from search result @graph
    },

    render: function () {
        const { items } = this.props;
        if (items && items.length) {
            return (

                    {items.map(item => (
                        <NewsPreviewItem key={item['@id']} item={item} />
                    ))}

            );
        }
        return
No news available at this time
;
    },
});
export default NewsPreviews;

// Default news facet-rendering component for any type of facet not specifically registered to render
// in its own component, so the vast majority of news facets use this component.
const DefaultFacet = React.createClass({
    propTypes: {
        facet: React.PropTypes.object.isRequired, // One facet from search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
        searchFilters: React.PropTypes.object.isRequired, // Search-result filter object; see <NewsFacets />
    },

    render: function () {
        const { facet, baseUri, searchFilters } = this.props;

        // Get the query string portion of the base URI, if any. This tells us if we need to append
        // new query string parameters with a '?' or a '&'.
        const query = url.parse(baseUri).search;

        return (

                {facet.nonEmptyTerms.map((term) => {
                    // Make the facet term link either the current URI appended with
                    // the facet field and term, or from any matching filter’s `remove`
                    // URI. Have to parse the `remove` URI to trim any vestiges of add
                    // query string.
                    let termFilter = searchFilters[facet.field] && searchFilters[facet.field][term.key] && searchFilters[facet.field][term.key].remove;
                    if (termFilter && termFilter[termFilter.length - 1] === '?') {
                        termFilter = termFilter.slice(0, -1);
                    }
                    const qs = globals.encodedURIComponent(`${facet.field}=${term.key}`);
                    const termHref = termFilter || `${baseUri}${query ? '&' : '?'}${qs}`;

                    return (

                                    {term.key}

                                    ({term.doc_count})

                    );
                })}

        );
    },
});

// Register this component to be the fallback facet-display component used when no other facet
// field has been registered to specifically render that kind of facet.
globals.facet_view.register(DefaultFacet, '');
globals.facet_view.fallback = () => DefaultFacet;

// Special news facet-rendering component to render facets with facet.field==='month_released'.
const DateFacet = React.createClass({
    propTypes: {
        facet: React.PropTypes.object.isRequired, // One facet from search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
        searchFilters: React.PropTypes.object.isRequired, // Search-result filter object; see <NewsFacets />
    },

    render: function () {
        const { facet, baseUri, searchFilters } = this.props;

        // Get the query string portion of the base URI, if any. This tells us if we need to append
        // new query string parameters with a '?' or a '&'.
        const query = url.parse(baseUri).search;

        // If >= 12 (a year) of facets to display, set a flag to know that we need to coalesce
        // older months into years.
        const coalesce = facet.nonEmptyTerms.length > 12;

        // Generate lists of facet terms to render based on whether we have to coalesce past years
        // or not.
        let trimmedMonthTerms = [];
        let coYearGroups = {};
        let inYearGroups = {};
        let years = [];
        if (coalesce) {
            // We have enough months in a facet to justify coalescing past years. First make a
            // YYYY-MM string for 11 months before now for something to compare against.
            const earliestMonth = moment().subtract(11, 'months').format('YYYY-MM');

            // Break terms into two keys in an object: one with the key `in` that's an array of all
            // terms that get rendered as individual months within the past 11 months, and one with
            // the key `co` with all the terms that need to be coalesced into years.
            const trimmedMonthGroups = _(facet.nonEmptyTerms).groupBy(term => ((term.key >= earliestMonth) ? 'in' : 'co'));

            // Get the terms from the past 11 months we individually render.
            trimmedMonthTerms = trimmedMonthGroups.in;

            // Group the coalesced terms into an object keyed by year, and also make a reverse-
            // sorted list of years. Also group the individual terms in case we need to sum up
            // overlapping years.
            coYearGroups = _(trimmedMonthGroups.co).groupBy(term => term.key.substr(0, 4));
            inYearGroups = _(trimmedMonthGroups.in).groupBy(term => term.key.substr(0, 4));
            years = Object.keys(coYearGroups).sort((a, b) => ((a < b) ? 1 : (b  ((a.key < b.key) ? 1 : (b.key  {
                    // Make the facet term link either the current URI appended with
                    // the facet field and term, or from any matching filter’s `remove`
                    // URI. Have to parse the `remove` URI to trim any vestiges of add
                    // query string.
                    let termFilter = searchFilters[facet.field] && searchFilters[facet.field][term.key] && searchFilters[facet.field][term.key].remove;
                    if (termFilter && termFilter[termFilter.length - 1] === '?') {
                        termFilter = termFilter.slice(0, -1);
                    }
                    const qs = globals.encodedURIComponent(`${facet.field}=${term.key}`);
                    const termHref = termFilter || `${baseUri}${query ? '&' : '?'}${qs}`;

                    return (

                                    {term.key}

                                    ({term.doc_count})

                    );
                })}
                {years.map((year) => {
                    // Sum up the doc_counts for the year being rendered. If we have overlapping
                    // term years, we have to add those to the total too.
                    let yearTotal = coYearGroups[year].reduce((total, term) => total + term.doc_count, 0);
                    if (inYearGroups[year]) {
                        yearTotal += inYearGroups[year].reduce((total, term) => total + term.doc_count, 0);
                    }

                    return (

                                    All {year}

                                    ({yearTotal})

                    );
                })}

        );
    },
});

globals.facet_view.register(DateFacet, 'month_released');

// Display the news facets.
const NewsFacets = React.createClass({
    propTypes: {
        facets: React.PropTypes.array.isRequired, // Array of facets, each containing an array of facet items
        filters: React.PropTypes.array.isRequired, // Array of filters from the search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
    },

    render: function () {
        const { facets, filters, baseUri } = this.props;

        // Filter out facets that have all-zero term counts. Also attach a filtered list of non-
        // empty terms to each facet so that we only display those.
        const nonEmptyFacets = facets.filter((facet) => {
            // Filter out terms with zero counts.
            const nonEmptyTerms = facet.terms.filter(term => term.doc_count);

            // Attach the filtered term list to the facet, and return whether there were any terms
            // left after filtering. Also set @type of the facet so that we can use the view
            // registry lookup.
            facet.nonEmptyTerms = nonEmptyTerms;
            facet['@type'] = [facet.field];
            return !!nonEmptyTerms.length;
        });

        // Now generate an object representing the search-result filters that's dereferenceable
        // as filters[facet.field][term.key] to avoid doing a search of the filters array for each
        // inner iteration of the facet display loop. If we have no query string, filters can have
        // zero elements in it.
        const searchFilters = {};
        if (filters.length) {
            filters.forEach((filter) => {
                if (!searchFilters[filter.field]) {
                    // A key for the filter's field doesn't exist, so make it an empty object we can add to
                    // on the next line.
                    searchFilters[filter.field] = {};
                }
                searchFilters[filter.field][filter.term] = filter;
            });
        }

        return (

                {nonEmptyFacets.map((facet) => {
                    const FacetView = globals.facet_view.lookup(facet);

                    return (

                                {facet.title}

                            <FacetView facet={facet} baseUri={baseUri} searchFilters={searchFilters} />

                    );
                })}

        );
    },
});

// Rendering component for the stand-alone news preview page.
const News = React.createClass({
    propTypes: {
        context: React.PropTypes.object, // Search results
    },

    render: function () {
        const { context } = this.props;
        const items = context['@graph'];
        const facets = context.facets;

        // Only display the full news page if the current query string and data give us news items
        // to display.
        if (context['@graph'].length && facets) {
            return (
                <Panel>
                    <PanelBody>

News

                                <NewsPreviews items={items} />

                                <NewsFacets facets={facets} filters={context.filters} baseUri={context['@id']} />

                    </PanelBody>
                </Panel>
            );
        }

        // Either the query string or the current data aren't giving us any news items to display.
        // Just show a message.
        return (
            <Panel>

No news items to display

                Latest ENCODE News
            </Panel>
        );
    },
});

globals.content_views.register(News, 'News');
<commit_msg>Make month selections exclusive<commit_after>import React from 'react';
import moment from 'moment';
import querystring from 'querystring';
import _ from 'underscore';
import url from 'url';
import globals from './globals';
import { Panel, PanelBody } from '../libs/bootstrap/panel';

// Display a news preview item from a search result @graph object.
const NewsPreviewItem = React.createClass({
    propTypes: {
        item: React.PropTypes.object, // News search result object to display
    },

    render: function () {
        const { item } = this.props;
        return (

{item.title}

{moment.utc(item.date_created).format('MMMM D, YYYY')}

{item.news_excerpt}

                    <a className="btn btn-info btn-sm" href={item['@id']} title={`View news post for ${item.title}`} key={item['@id']}>Read more

        );
    },
});

// Display a list of news preview items from search results.
const NewsPreviews = React.createClass({
    propTypes: {
        items: React.PropTypes.array, // Items from search result @graph
    },

    render: function () {
        const { items } = this.props;
        if (items && items.length) {
            return (

                    {items.map(item => (
                        <NewsPreviewItem key={item['@id']} item={item} />
                    ))}

            );
        }
        return
No news available at this time
;
    },
});
export default NewsPreviews;

// Default news facet-rendering component for any type of facet not specifically registered to render
// in its own component, so the vast majority of news facets use this component.
const DefaultFacet = React.createClass({
    propTypes: {
        facet: React.PropTypes.object.isRequired, // One facet from search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
        searchFilters: React.PropTypes.object.isRequired, // Search-result filter object; see <NewsFacets />
    },

    render: function () {
        const { facet, baseUri, searchFilters } = this.props;

        // Get the query string portion of the base URI, if any. This tells us if we need to append
        // new query string parameters with a '?' or a '&'.
        const query = url.parse(baseUri).search;

        return (

                    {facet.title}

                    {facet.nonEmptyTerms.map((term) => {
                        // Make the facet term link either the current URI appended with
                        // the facet field and term, or from any matching filter’s `remove`
                        // URI. Have to parse the `remove` URI to trim any vestiges of add
                        // query string.
                        let termFilter = searchFilters[facet.field] && searchFilters[facet.field][term.key] && searchFilters[facet.field][term.key].remove;
                        if (termFilter && termFilter[termFilter.length - 1] === '?') {
                            termFilter = termFilter.slice(0, -1);
                        }
                        const qs = globals.encodedURIComponent(`${facet.field}=${term.key}`);
                        const termHref = termFilter || `${baseUri}${query ? '&' : '?'}${qs}`;

                        return (

                                        {term.key}

                                        ({term.doc_count})

                        );
                    })}

        );
    },
});

// Register this component to be the fallback facet-display component used when no other facet
// field has been registered to specifically render that kind of facet.
globals.facet_view.register(DefaultFacet, '');
globals.facet_view.fallback = () => DefaultFacet;

// Special news facet-rendering component to render facets with facet.field==='month_released'.
const DateFacet = React.createClass({
    propTypes: {
        facet: React.PropTypes.object.isRequired, // One facet from search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
        searchFilters: React.PropTypes.object.isRequired, // Search-result filter object; see <NewsFacets />
    },

    render: function () {
        const { facet, baseUri, searchFilters } = this.props;
        let yearRemoveUri = '';
        let monthRemoveUri = '';
        let queryParms;
        let yearsSelected;

        // Generate the href for the "All years" facet term if we need one. This involves removing
        // all (normally one) year_released parameters from the query string. Start by getting the
        // query string portion of the base URI, if any.
        const baseUriComps = url.parse(baseUri);
        if (baseUriComps.search) {
            // A query string exists. Parse it to see if it has one or more year_released
            // parameters. If it does, delete it. Have to strip the first character off the query
            // string because it's always a question mark which querystring.parse can't handle.
            queryParms = querystring.parse(baseUriComps.search.substring(1));
            if (queryParms.year_released) {
                // At least one "year_released" query string parameter found. First get what their
                // values are so we can display it in the facet title.
                yearsSelected = (typeof queryParms.year_released === 'string') ? [queryParms.year_released] : queryParms.year_released;

                // Now delete the year_released query string parameters and generate an href
                // without them to use to clear the year while leaving other query string
                // parameters intact. Use this for the 'All years' static facet item.
                delete queryParms.year_released;
                const updatedQs = querystring.stringify(queryParms);
                yearRemoveUri = `${baseUriComps.pathname}${updatedQs ? `?${updatedQs}` : ''}`;
            }

            // Now calculate a Uri with month_released removed, for individual month facet term
            // hrefs while another individual month is selected.
            if (queryParms.month_released) {
                delete queryParms.month_released;
                monthRemoveUri = `${baseUriComps.pathname}?${querystring.stringify(queryParms)}`;
            } else {
                monthRemoveUri = baseUri;
            }
        }
        // If no baseUriComps.search exists, then no query string exists.

        // If >= 12 (a year) of facets to display, set a flag to know that we need to coalesce
        // older months into years. Don't coalesce if a year is currently selected.
        const coalesce = !yearsSelected && facet.nonEmptyTerms.length > 12;

        // Generate lists of facet terms to render based on whether we have to coalesce past years
        // or not.
        let trimmedMonthTerms = [];
        let coYearGroups = {};
        let inYearGroups = {};
        let years = [];
        if (coalesce) {
            // We have enough months in a facet to justify coalescing past years. First make a
            // YYYY-MM string for 11 months before now for something to compare against.
            const earliestMonth = moment().subtract(11, 'months').format('YYYY-MM');

            // Break terms into two keys in an object: one with the key `in` that's an array of all
            // terms that get rendered as individual months within the past 11 months, and one with
            // the key `co` with all the terms that need to be coalesced into years.
            const trimmedMonthGroups = _(facet.nonEmptyTerms).groupBy(term => ((term.key >= earliestMonth) ? 'in' : 'co'));

            // Get the terms from the past 11 months we individually render.
            trimmedMonthTerms = trimmedMonthGroups.in;

            // Group the coalesced terms into an object keyed by year, and also make a reverse-
            // sorted list of years. Also group the individual terms in case we need to sum up
            // overlapping years.
            coYearGroups = _(trimmedMonthGroups.co).groupBy(term => term.key.substr(0, 4));
            inYearGroups = _(trimmedMonthGroups.in).groupBy(term => term.key.substr(0, 4));
            years = Object.keys(coYearGroups).sort((a, b) => ((a < b) ? 1 : (b  ((a.key < b.key) ? 1 : (b.key < a.key ? -1 : 0)));

        return (

                    Months
                    {yearsSelected ?  for {yearsSelected.join(', ')} : null}

                    {trimmedMonthTerms.map((term) => {
                        // Loop to display the individual month terms. Make the facet term link
                        // either the current URI appended with the facet field and term, or from
                        // any matching filter’s `remove` URI. Have to parse the `remove` URI to
                        // trim any vestiges of add query string.
                        let termFilter = searchFilters[facet.field] && searchFilters[facet.field][term.key] && searchFilters[facet.field][term.key].remove;
                        if (termFilter && termFilter[termFilter.length - 1] === '?') {
                            termFilter = termFilter.slice(0, -1);
                        }

                        const qs = globals.encodedURIComponent(`${facet.field}=${term.key}`);
                        const termHref = termFilter || `${monthRemoveUri}${baseUriComps.search ? '&' : '?'}${qs}`;

                        return (

                                        {term.key}

                                        ({term.doc_count})

                        );
                    })}

                    {years.map((year) => {
                        // Loop to display the coalesced year terms. Sum up the doc_counts for the
                        // year being rendered. If we have overlapping term years, we have to add
                        // those to the total too.
                        let yearTotal = coYearGroups[year].reduce((total, term) => total + term.doc_count, 0);
                        if (inYearGroups[year]) {
                            yearTotal += inYearGroups[year].reduce((total, term) => total + term.doc_count, 0);
                        }

                        // Calculate the href. Works differently for coalesced facet terms because
                        // only one can be selected at a time.
                        const qs = globals.encodedURIComponent(`year_released=${year}`);
                        const termHref = `${baseUri}${baseUriComps.search ? '&' : '?'}${qs}`;

                        return (

                                        All {year}

                                        ({yearTotal})

                        );
                    })}

                    {yearsSelected ?

                                All years

                    : null}

        );
    },
});

globals.facet_view.register(DateFacet, 'month_released');

// Display the news facets.
const NewsFacets = React.createClass({
    propTypes: {
        facets: React.PropTypes.array.isRequired, // Array of facets, each containing an array of facet items
        filters: React.PropTypes.array.isRequired, // Array of filters from the search results
        baseUri: React.PropTypes.string.isRequired, // Current URI and query string
    },

    render: function () {
        const { facets, filters, baseUri } = this.props;

        // Filter out facets that have all-zero term counts. Also attach a filtered list of non-
        // empty terms to each facet so that we only display those.
        const nonEmptyFacets = facets.filter((facet) => {
            // Filter out terms with zero counts.
            const nonEmptyTerms = facet.terms.filter(term => term.doc_count);

            // Attach the filtered term list to the facet, and return whether there were any terms
            // left after filtering. Also set @type of the facet so that we can use the view
            // registry lookup.
            facet.nonEmptyTerms = nonEmptyTerms;
            facet['@type'] = [facet.field];
            return !!nonEmptyTerms.length;
        });

        // Now generate an object representing the search-result filters that's dereferenceable
        // as filters[facet.field][term.key] to avoid doing a search of the filters array for each
        // inner iteration of the facet display loop. If we have no query string, filters can have
        // zero elements in it.
        const searchFilters = {};
        if (filters.length) {
            filters.forEach((filter) => {
                if (!searchFilters[filter.field]) {
                    // A key for the filter's field doesn't exist, so make it an empty object we can add to
                    // on the next line.
                    searchFilters[filter.field] = {};
                }
                searchFilters[filter.field][filter.term] = filter;
            });
        }

        return (

                {nonEmptyFacets.map((facet) => {
                    const FacetView = globals.facet_view.lookup(facet);

                    return <FacetView facet={facet} baseUri={baseUri} searchFilters={searchFilters} />;
                })}

        );
    },
});

// Rendering component for the stand-alone news preview page.
const News = React.createClass({
    propTypes: {
        context: React.PropTypes.object, // Search results
    },

    render: function () {
        const { context } = this.props;
        const items = context['@graph'];
        const facets = context.facets;

        // Only display the full news page if the current query string and data give us news items
        // to display.
        if (context['@graph'].length && facets) {
            return (
                <Panel>
                    <PanelBody>

News

                                <NewsPreviews items={items} />

                                <NewsFacets facets={facets} filters={context.filters} baseUri={context['@id']} />

                    </PanelBody>
                </Panel>
            );
        }

        // Either the query string or the current data aren't giving us any news items to display.
        // Just show a message.
        return (
            <Panel>

No news items to display

                Latest ENCODE News
            </Panel>
        );
    },
});

globals.content_views.register(News, 'News');
<|endoftext|>


<commit_before>var p;
var drawings = [];

var DrawingManager = function (p5Sketch) {
  p = p5Sketch;
};

DrawingManager.prototype.add = function (pulse) {
  console.log('Added ' + pulse);
  drawings.push(pulse);
}

DrawingManager.prototype.drawAll = function () {
  for (var i = drawings.length - 1; i >= 0; i--) {
    drawings[i].draw(p);
  };
}

module.exports = DrawingManager;<commit_msg>Add add garbage collection and fix drawing order in the manager<commit_after>var p;
var drawings = [];

var DrawingManager = function (p5Sketch) {
  p = p5Sketch;
};

DrawingManager.prototype.add = function (drawing) {
  console.log('Added ' + drawing);
  drawings.unshift(drawing);
}

DrawingManager.prototype.drawAll = function () {
  for (var i = drawings.length - 1; i >= 0; i--) {
    drawings[i].draw(p);

    if (drawings[i].done()) {
      drawings.splice(i, 1);
      i--;
    }
  };
}

module.exports = DrawingManager;<|endoftext|>


<commit_before>const fs = require("fs");
const path = require("path");
const glob = require("glob");
const _get = require("lodash/get");
const filenameMatchesPattern =
  require("../graph-utl/match-facade").filenameMatchesPattern;
const pathToPosix = require("./utl/path-to-posix");
const transpileMeta = require("./transpile/meta");

/**
 *
 * @param {import('../../types/options').IStrictCruiseOptions} pOptions
 * @returns {string[]}
 */
function getScannableExtensions(pOptions) {
  return transpileMeta.scannableExtensions.concat(
    pOptions.extraExtensionsToScan
  );
}

function shouldBeIncluded(pFullPathToFile, pOptions) {
  return (
    !_get(pOptions, "includeOnly.path") ||
    filenameMatchesPattern(pFullPathToFile, pOptions.includeOnly.path)
  );
}

function shouldNotBeExcluded(pFullPathToFile, pOptions) {
  return (
    (!_get(pOptions, "exclude.path") ||
      !filenameMatchesPattern(pFullPathToFile, pOptions.exclude.path)) &&
    (!_get(pOptions, "doNotFollow.path") ||
      !filenameMatchesPattern(pFullPathToFile, pOptions.doNotFollow.path))
  );
}

/**
 *
 * @param {string} pDirectoryName
 * @param  {import('../../types/options').IStrictCruiseOptions} pOptions options that
 * @returns {string[]}
 */
function gatherScannableFilesFromDirectory(pDirectoryName, pOptions) {
  return fs
    .readdirSync(path.join(pOptions.baseDir, pDirectoryName))
    .map((pFileName) => path.join(pDirectoryName, pFileName))
    .filter((pFullPathToFile) =>
      shouldNotBeExcluded(pathToPosix(pFullPathToFile), pOptions)
    )
    .reduce((pSum, pFullPathToFile) => {
      let lStat = {};
      try {
        lStat = fs.statSync(path.join(pOptions.baseDir, pFullPathToFile));
      } catch (pError) {
        return pSum;
      }
      if (lStat.isDirectory()) {
        return pSum.concat(
          gatherScannableFilesFromDirectory(pFullPathToFile, pOptions)
        );
      }
      if (
        getScannableExtensions(pOptions).some((pExtension) =>
          pFullPathToFile.endsWith(pExtension)
        )
      ) {
        return pSum.concat(pFullPathToFile);
      }
      return pSum;
    }, [])
    .map((pFullPathToFile) => pathToPosix(pFullPathToFile))
    .filter((pFullPathToFile) => shouldBeIncluded(pFullPathToFile, pOptions));
}

/**
 * Returns an array of strings, representing paths to files to be gathered
 *
 * If an entry in the array passed is a (path to a) directory, it recursively
 * scans that directory for files with a scannable extension.
 * If an entry is a path to a file it just adds it.
 *
 * Files and directories are assumed to be either absolute, or relative to the
 * current working directory.
 *
 * @param  {string[]} pFileAndDirectoryArray globs and/ or paths to files or
 *                               directories to be gathered
 * @param  {import('../../types/dependency-cruiser').IStrictCruiseOptions} pOptions options that
 *                               influence what needs to be gathered/ scanned
 *                               notably useful attributes:
 *                               - exclude - regexp of what to exclude
 *                               - includeOnly - regexp what to include
 * @return {string[]}            paths to files to be gathered.
 */
module.exports = function gatherInitialSources(
  pFileAndDirectoryArray,
  pOptions
) {
  const lOptions = { baseDir: process.cwd(), ...pOptions };

  return pFileAndDirectoryArray
    .reduce(
      (pAll, pFileOrDirectory) =>
        pAll.concat(glob.sync(pFileOrDirectory, { cwd: lOptions.baseDir })),
      []
    )
    .reduce((pAll, pFileOrDirectory) => {
      if (
        fs.statSync(path.join(lOptions.baseDir, pFileOrDirectory)).isDirectory()
      ) {
        return pAll.concat(
          gatherScannableFilesFromDirectory(pFileOrDirectory, lOptions)
        );
      } else {
        return pAll.concat(pathToPosix(pFileOrDirectory));
      }
    }, [])
    .sort();
};
<commit_msg>refactor(extract): use getExtension instead of endsWith to determine scannability of extensions (#609)<commit_after>const fs = require("fs");
const path = require("path");
const glob = require("glob");
const get = require("lodash/get");
const filenameMatchesPattern =
  require("../graph-utl/match-facade").filenameMatchesPattern;
const pathToPosix = require("./utl/path-to-posix");
const transpileMeta = require("./transpile/meta");
const getExtension = require("./utl/get-extension");

/**
 *
 * @param {import('../../types/options').IStrictCruiseOptions} pOptions
 * @returns {string[]}
 */
function getScannableExtensions(pOptions) {
  return transpileMeta.scannableExtensions.concat(
    pOptions.extraExtensionsToScan
  );
}

function fileIsScannable(pOptions, pPathToFile) {
  return getScannableExtensions(pOptions).includes(getExtension(pPathToFile));
}

function shouldBeIncluded(pFullPathToFile, pOptions) {
  return (
    !get(pOptions, "includeOnly.path") ||
    filenameMatchesPattern(pFullPathToFile, pOptions.includeOnly.path)
  );
}

function shouldNotBeExcluded(pFullPathToFile, pOptions) {
  return (
    (!get(pOptions, "exclude.path") ||
      !filenameMatchesPattern(pFullPathToFile, pOptions.exclude.path)) &&
    (!get(pOptions, "doNotFollow.path") ||
      !filenameMatchesPattern(pFullPathToFile, pOptions.doNotFollow.path))
  );
}

/**
 *
 * @param {string} pDirectoryName
 * @param  {import('../../types/options').IStrictCruiseOptions} pOptions options that
 * @returns {string[]}
 */
function gatherScannableFilesFromDirectory(pDirectoryName, pOptions) {
  return fs
    .readdirSync(path.join(pOptions.baseDir, pDirectoryName))
    .map((pFileName) => path.join(pDirectoryName, pFileName))
    .filter((pFullPathToFile) =>
      shouldNotBeExcluded(pathToPosix(pFullPathToFile), pOptions)
    )
    .reduce((pSum, pFullPathToFile) => {
      let lStat = {};
      try {
        lStat = fs.statSync(path.join(pOptions.baseDir, pFullPathToFile));
      } catch (pError) {
        return pSum;
      }
      if (lStat.isDirectory()) {
        return pSum.concat(
          gatherScannableFilesFromDirectory(pFullPathToFile, pOptions)
        );
      }
      if (fileIsScannable(pOptions, pFullPathToFile)) {
        return pSum.concat(pFullPathToFile);
      }
      return pSum;
    }, [])
    .map((pFullPathToFile) => pathToPosix(pFullPathToFile))
    .filter((pFullPathToFile) => shouldBeIncluded(pFullPathToFile, pOptions));
}

/**
 * Returns an array of strings, representing paths to files to be gathered
 *
 * If an entry in the array passed is a (path to a) directory, it recursively
 * scans that directory for files with a scannable extension.
 * If an entry is a path to a file it just adds it.
 *
 * Files and directories are assumed to be either absolute, or relative to the
 * current working directory.
 *
 * @param  {string[]} pFileAndDirectoryArray globs and/ or paths to files or
 *                               directories to be gathered
 * @param  {import('../../types/dependency-cruiser').IStrictCruiseOptions} pOptions options that
 *                               influence what needs to be gathered/ scanned
 *                               notably useful attributes:
 *                               - exclude - regexp of what to exclude
 *                               - includeOnly - regexp what to include
 * @return {string[]}            paths to files to be gathered.
 */
module.exports = function gatherInitialSources(
  pFileAndDirectoryArray,
  pOptions
) {
  const lOptions = { baseDir: process.cwd(), ...pOptions };

  return pFileAndDirectoryArray
    .reduce(
      (pAll, pFileOrDirectory) =>
        pAll.concat(glob.sync(pFileOrDirectory, { cwd: lOptions.baseDir })),
      []
    )
    .reduce((pAll, pFileOrDirectory) => {
      if (
        fs.statSync(path.join(lOptions.baseDir, pFileOrDirectory)).isDirectory()
      ) {
        return pAll.concat(
          gatherScannableFilesFromDirectory(pFileOrDirectory, lOptions)
        );
      } else {
        return pAll.concat(pathToPosix(pFileOrDirectory));
      }
    }, [])
    .sort();
};
<|endoftext|>


<commit_before>// DEPRECATED

THREE.CubeGeometry = function(){
	console.warn( 'DEPRECATED: THREE.CubeGeometry is deprecated. Use THREE.BoxGeometry instead.' );
	return new THREE.BoxGeometry();
};
<commit_msg>Finished CubeGeometry deprecated change.<commit_after>// DEPRECATED

THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
	console.warn( 'DEPRECATED: THREE.CubeGeometry is deprecated. Use THREE.BoxGeometry instead.' );
 	return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
 };<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.logger',
  name: 'DiscardLogger',
  extends: 'foam.nanos.logger.ProxyLogger',
  requires: [
    'foam.nanos.logger.LogLevel'
  ],

  properties: [
    {
      class: 'Object',
      name: 'lastUniqueObject',
      value: null
    },
    {
      class: 'Int',
      name: 'repeatCount',
      value: 0
    },
    {
      class: 'Enum',
      of: 'foam.nanos.logger.LogLevel',
      name: 'lastLogLevel',
      factory: function() { return foam.nanos.logger.LogLevel.INFO; },
      // javaFactory: `return foam.nanos.logger.LogLevel.INFO;`
    }
  ],

  methods: [
    {
      name: 'newLogHandler',
      args: [
        {
          name: 'logLevelName',
          javaType: 'Enum'
        },
        {
          name: 'setLastLogLevel',
          javaType: 'Boolean'
        },
        {
          name: 'args',
          javaType: 'Object...'
        },
      ],
      javaReturns: 'void',
      javaCode: `
  switch ( LogLevel.values()[logLevelName.ordinal()] ) {
    case DEBUG: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().debug(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.DEBUG);
      }
      break;
    }
    case INFO: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().info(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.INFO);
      }
      break;
    }
    case WARNING: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().warning(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.WARNING);
      }
      break;

    }
    case ERROR: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().error(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.ERROR);
      }
      break;
    }
  }
`
    },
    {
      name: 'logLastLogRepeats',
      javaReturns: 'void',
      javaCode:
      `
  switch ( getLastLogLevel() ) {
    case DEBUG: {
      getDelegate().debug("The last log was repeated " + getRepeatCount() + " times" );
      break;
    }
    case INFO: {
      getDelegate().info("The last log was repeated "+ getRepeatCount()  + " times" );
      break;
    }
    case WARNING: {
      getDelegate().warning("The last log was repeated "+ getRepeatCount()  + " times" );
      break;
    }
    case ERROR: {
      getDelegate().error("The last log was repeated " + getRepeatCount() + " times" );
      break;
    }
  }
`
    },
    {
      name: 'repeatLogConstructor',
      args: [
        {
          name: 'logLevelName',
          javaType: 'Enum'
        },
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      synchronized: true,
      javaReturns: 'void',
      javaCode: `
  if ( getLastLogLevel().ordinal() != logLevelName.ordinal() ) {
    if ( getRepeatCount() > 1 ) {
      logLastLogRepeats();
    }
    newLogHandler(logLevelName, true, args);
    return;
  };

  if ( getLastUniqueObject() == null ) {
    newLogHandler(logLevelName,false,args);
  } else {
    Object[] o = (Object[]) getLastUniqueObject();
    if ( ! (args.length == o.length ) ) {
      // Different Message
      if ( getRepeatCount() > 1 ) {
        logLastLogRepeats();
      }
      newLogHandler(logLevelName,false,args);
    } else {
      for ( int i = 0 ; i < args.length ; i++ ) {
        if ( ! ( args[i].equals(o[i]) && !(args[i] instanceof Exception) ) ) {
          // simplify above logic
          if ( getRepeatCount() > 1 ) {
            logLastLogRepeats();
          }
          newLogHandler(logLevelName, false, args);
          return;
        }
      }
      setRepeatCount( getRepeatCount() + 1 );
    }
  }
`
    },
    {
      name: 'debug',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.DEBUG;
  repeatLogConstructor(logLevelName, args);
  `
    },
    {
      name: 'info',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.INFO;
  repeatLogConstructor(logLevelName,args);
`
    },
    {
      name: 'warning',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.WARNING;
  repeatLogConstructor(logLevelName,args);
`
    },
    {
      name: 'error',
      args:[
        {
          name: 'args',
          javaType: 'Object...',
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.ERROR;
  repeatLogConstructor(logLevelName,args);
`
    }
  ]
});

// implement hashcode to check instead of equals<commit_msg>remove javaFactory code for enums, add comment<commit_after>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.logger',
  name: 'RepeatLogger',
  extends: 'foam.nanos.logger.ProxyLogger',
  requires: [
    'foam.nanos.logger.LogLevel'
  ],

  properties: [
    {
      class: 'Object',
      name: 'lastUniqueObject',
      value: null
    },
    {
      class: 'Int',
      name: 'repeatCount',
      value: 0
    },
    {
      class: 'Enum',
      of: 'foam.nanos.logger.LogLevel',
      name: 'lastLogLevel',
      factory: function() { return foam.nanos.logger.LogLevel.INFO; }
    }
  ],

  methods: [
    {
      name: 'newLogHandler',
      args: [
        {
          name: 'logLevelName',
          javaType: 'Enum'
        },
        {
          name: 'setLastLogLevel',
          javaType: 'Boolean'
        },
        {
          name: 'args',
          javaType: 'Object...'
        },
      ],
      javaReturns: 'void',
      javaCode: `
  switch ( LogLevel.values()[logLevelName.ordinal()] ) {
    case DEBUG: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().debug(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.DEBUG);
      }
      break;
    }
    case INFO: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().info(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.INFO);
      }
      break;
    }
    case WARNING: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().warning(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.WARNING);
      }
      break;

    }
    case ERROR: {
      setRepeatCount(1);
      setLastUniqueObject(args);
      getDelegate().error(args);
      if (setLastLogLevel){
        setLastLogLevel(LogLevel.ERROR);
      }
      break;
    }
  }
`
    },
    {
      name: 'logLastLogRepeats',
      javaReturns: 'void',
      javaCode:
      `
  switch ( getLastLogLevel() ) {
    case DEBUG: {
      getDelegate().debug("The last log was repeated " + getRepeatCount() + " times" );
      break;
    }
    case INFO: {
      getDelegate().info("The last log was repeated "+ getRepeatCount()  + " times" );
      break;
    }
    case WARNING: {
      getDelegate().warning("The last log was repeated "+ getRepeatCount()  + " times" );
      break;
    }
    case ERROR: {
      getDelegate().error("The last log was repeated " + getRepeatCount() + " times" );
      break;
    }
  }
`
    },
    {
      name: 'repeatLogConstructor',
      args: [
        {
          name: 'logLevelName',
          javaType: 'Enum'
        },
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      synchronized: true,
      javaReturns: 'void',
      javaCode: `
  if ( getLastLogLevel().ordinal() != logLevelName.ordinal() ) {
    if ( getRepeatCount() > 1 ) {
      logLastLogRepeats();
    }
    newLogHandler(logLevelName, true, args);
    return;
  };

  if ( getLastUniqueObject() == null ) {
    newLogHandler(logLevelName,false,args);
  } else {
    Object[] o = (Object[]) getLastUniqueObject();
    if ( ! (args.length == o.length ) ) {
      // Different Message
      if ( getRepeatCount() > 1 ) {
        logLastLogRepeats();
      }
      newLogHandler(logLevelName,false,args);
    } else {
      for ( int i = 0 ; i < args.length ; i++ ) {
        if ( ! ( args[i].equals(o[i]) && !(args[i] instanceof Exception) ) ) {
          // simplify above logic
          if ( getRepeatCount() > 1 ) {
            logLastLogRepeats();
          }
          newLogHandler(logLevelName, false, args);
          return;
        }
      }
      setRepeatCount( getRepeatCount() + 1 );
    }
  }
`
    },
    {
      name: 'debug',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.DEBUG;
  repeatLogConstructor(logLevelName, args);
  `
    },
    {
      name: 'info',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.INFO;
  repeatLogConstructor(logLevelName,args);
`
    },
    {
      name: 'warning',
      args:[
        {
          name: 'args',
          javaType: 'Object...'
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.WARNING;
  repeatLogConstructor(logLevelName,args);
`
    },
    {
      name: 'error',
      args:[
        {
          name: 'args',
          javaType: 'Object...',
        }
      ],
      javaReturns: 'void',
      javaCode: `
  Enum logLevelName = LogLevel.ERROR;
  repeatLogConstructor(logLevelName,args);
`
    }
  ]
});

// is there somewhere a check of hashcode equality is preferable?
// remeber Joel mentioning it, but can't remember the exact context.<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.crunch',
  name: 'CapabilityStore',
  extends: 'foam.u2.View',

  implements: [
    'foam.mlang.Expressions'
  ],

  requires: [
    'foam.nanos.crunch.Capability',
    'foam.nanos.crunch.CapabilityCategory',
    'foam.nanos.crunch.CapabilityCategoryCapabilityJunction',
    'foam.u2.crunch.CapabilityCardView',
    'foam.u2.crunch.CapabilityFeatureView',
    'foam.u2.Element',
    'foam.u2.layout.Grid',
    'foam.u2.layout.GUnit',
    'foam.u2.Tab',
    'foam.u2.Tabs',
    'foam.u2.UnstyledTabs'
  ],

  imports: [
    'auth',
    'ctrl',
    'capabilityCategoryCapabilityJunctionDAO',
    'capabilityCategoryDAO',
    'capabilityDAO',
    'crunchController',
    'registerElement'
  ],

  messages: [
    { name: 'TAB_ALL', message: 'All' }
  ],

  css: `
    ^ {
      margin: auto;
      padding: 12px 24px 24px 24px;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
    }

    ^feature-column-grid {
      display: inline-flex;
      width: 94%;
      overflow: hidden;
    }

    ^featureSection {
      flex: 0;
      height: auto;
    }

    ^perFeature {
      display: flex;
      padding-bottom: 10px;
    }

    ^left-arrow {
      width: 3%;
      float: left;
      transform: scaleX(-1);
      display: flex;
      padding-top: 70px;
      /* HOVER OFF */
      -webkit-transition: padding 2s;
    }

    ^right-arrow {
      width: 3%;
      float: right;
      display: flex;
      padding-top: 70px;
      margin-left: -20px;
      z-index: 10000;
      position: relative;
      /* HOVER OFF */
      -webkit-transition: padding 2s;
    }

    ^right-arrow:hover {
      transform: scale(1.2);
      /* HOVER ON */
      -webkit-transition: border-radius 2s;
    }

    ^left-arrow:hover {
      transform: scaleX(-1) scale(1.2);
      /* HOVER ON */
      -webkit-transition: border-radius 2s;
    }

    ^container {
      display: inline-block;
      width: 100%;
      height: fit-content;
      overflow-y: visible;
    }
  `,

  properties: [
    {
      name: 'visibleCapabilityDAO',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO with only visible capabilities.
      `,
      factory: function() {
        return this.capabilityDAO
          .where(this.EQ(this.Capability.VISIBLE, true));
      }
    },
    {
      name: 'featuredCapabilities',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO Property to find capabilities to feature.
      `,
      factory: function() {
        return this.visibleCapabilityDAO
          .where(this.IN('featured', this.Capability.KEYWORDS));
      }
    },
    {
      name: 'visibleCategoryDAO',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO Property to find categories that are visible.
      `,
      expression: function(capabilityCategoryDAO) {
        return capabilityCategoryDAO
          .where(this.EQ(this.CapabilityCategory.VISIBLE, true));
      }
    },
    {
      name: 'carouselCounter',
      class: 'Int',
      documentation: 'left and right scroll counter for featureCardArray index'
    },
    {
      name: 'totalNumCards',
      class: 'Int',
      documentation: 'should be equivalent to featureCardArray.length'
    },
    {
      name: 'featureCardArray',
      value: [],
      documentation: 'stores the styling of each featureCapability'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.signingOfficerQuestion();

      var self = this;
      window.cstore = self;

      self
        .addClass(self.myClass())
        .add(self.renderFeatured())
        .add(self.accountAndAccountingCard())
        // NOTE: TEMPORARILY REMOVED
        // .start(self.Tabs)
        //   .start(self.Tab, { label: this.TAB_ALL, selected: true })
        //     .add(self.renderFeatured())
        //     .add(self.accountAndAccountingCard())
        //   .end()
        //   // TODO: replace this .call with a .select once
        //   //       duplication error is fixed
        //   .call(function() {
        //     self.visibleCategoryDAO.select().then(a => {
        //       for ( let i = 0 ; i < a.array.length ; i++ ) {
        //         let category = a.array[i];
        //         let e = self.Tab.create({ label: category.name })
        //           .add(self.renderSection(category));
        //         this.add(e);
        //       }
        //     });
        //   })
        .end();
    },

    function renderFeatured() { // Featured Capabilities in carousel view
      var self = this;
      var spot = self.E();
      return this.E().start()// .style({ 'height': 'fit-content', 'overflow-y': 'visible' })
        .addClass(this.myClass('container'))
        .add(this.slot(function(featuredCapabilities) {
          featuredCapabilities.select().then(result => {
            var arr = result.array;
            self.totalNumCards = arr.length;
            self.featureCardArray = [];
            for ( let i = 0 ; i < self.totalNumCards ; i++ ) { // build featured cards as elements
              self.featureCardArray.push(
                () => self.Element.create().start()
                  .addClass(self.myClass('perFeature'))
                  .start(self.CapabilityFeatureView, { data: arr[i] })
                    .addClass(self.myClass('featureSection'))
                  .end()
                  .on('click', () => {
                    self.crunchController
                      .createWizardSequence(arr[i].id).execute();
                  })
                .end());
            }
            spot.start('span').start('img').addClass(self.myClass('left-arrow'))
                .attr('src', 'images/carouselArrow.svg')
                .on('click', function() {
                  self.carouselCounter--;
                })
              .end().end();
            spot.add(self.slot(
              function(carouselCounter, totalNumCards) {
                var ele = self.E().addClass(self.myClass('feature-column-grid'));
                for ( var k = 0 ; k < totalNumCards ; k++ ) {
                  let cc = carouselCounter % totalNumCards; // this stops any out of bounds indecies
                  let index = ( cc + totalNumCards + k ) % totalNumCards; // this ensures circle indecies
                  ele = ele.add(self.featureCardArray[index].call(self));
                }
                return ele;
              }));
            spot.start('span').start('img').addClass(self.myClass('right-arrow'))
              .attr('src', 'images/carouselArrow.svg')
              .on('click', function() {
                self.carouselCounter++;
              })
            .end().end();
          });
          return spot;
       })).end();
    },

    function accountAndAccountingCard() {
      // Capability Store Section Previews
      var self = this;
      return self.E()
        .select(self.visibleCategoryDAO$proxy, function(category) {
          var sectionElement = this.E('span');
          var returnElement = this.E()
            .start('h3')
              .add({ data : category, clsInfo : category.cls_.NAME.name, default : category.name })
            .end()
            .add(sectionElement);
          var previewIdsPromise = self.getCategoryDAO_(category.id).limit(6)
            .select().then(arraySink => arraySink.array.map(x => x.targetId) );

          previewIdsPromise.then(capabilityIds => {
            self.visibleCapabilityDAO.where(
              self.IN(self.Capability.ID, capabilityIds)
            ).select().then(result => {
              let arr = result.array;
              let grid = self.Grid.create();
              for ( let i = 0 ; i < arr.length ; i++ ) {
                let cap = arr[i];
                grid = grid
                  .start(self.GUnit, { columns: 4 })
                    .tag(self.CapabilityCardView, { data: cap })
                    .on('click', () => {
                      self.crunchController
                        .createWizardSequence(cap).execute();
                    })
                  .end();
              }
              sectionElement.add(grid);
            });
          });
          return returnElement;
        });
    },

    function renderSection(category) {
      var self = this;
      var sectionElement = this.E();

      // Promise 'p' reports capability IDs that are in this category
      var p = self.getCategoryDAO_(category.id).select(
        self.PROJECTION(self.CapabilityCategoryCapabilityJunction.TARGET_ID));

      // When 'p' resolves, query all matching capabilities
      p.then(arraySink => {
        capabilityIds = arraySink.projection;
        self.visibleCapabilityDAO.where(
          self.IN(self.Capability.ID, capabilityIds)
        ).select().then(result => {
          let arr = result.array;
          let grid = self.Grid.create();
          for ( let i = 0 ; i < arr.length ; i++ ) {
            let cap = arr[i];
            grid = grid
              .start(self.GUnit, { columns: 4 })
                .tag(self.CapabilityCardView, { data: cap })
                .on('click', () => {
                  self.crunchController
                    .createWizardSequence(cap).execute();
                })
              .end();
          }
          sectionElement.add(grid);
        });
      });
      return sectionElement;
    },
    function getCategoryDAO_(categoryId) {
      return this.capabilityCategoryCapabilityJunctionDAO.where(
        this.EQ(
          this.CapabilityCategoryCapabilityJunction.SOURCE_ID,
          categoryId));
    },
    function signingOfficerQuestion() {
      return this.auth.check(this.ctrl.__subContext__, 'businessHasSigningOfficer');
    }
  ]
});
<commit_msg>make non-featured sections line up with featured caps instead of carousel buttons (#3937)<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.crunch',
  name: 'CapabilityStore',
  extends: 'foam.u2.View',

  implements: [
    'foam.mlang.Expressions'
  ],

  requires: [
    'foam.nanos.crunch.Capability',
    'foam.nanos.crunch.CapabilityCategory',
    'foam.nanos.crunch.CapabilityCategoryCapabilityJunction',
    'foam.u2.crunch.CapabilityCardView',
    'foam.u2.crunch.CapabilityFeatureView',
    'foam.u2.Element',
    'foam.u2.layout.Grid',
    'foam.u2.layout.GUnit',
    'foam.u2.Tab',
    'foam.u2.Tabs',
    'foam.u2.UnstyledTabs'
  ],

  imports: [
    'auth',
    'ctrl',
    'capabilityCategoryCapabilityJunctionDAO',
    'capabilityCategoryDAO',
    'capabilityDAO',
    'crunchController',
    'registerElement'
  ],

  messages: [
    { name: 'TAB_ALL', message: 'All' }
  ],

  css: `
    ^ {
      margin: auto;
      padding: 12px 24px 24px 24px;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
    }

    ^feature-column-grid {
      display: inline-flex;
      width: 94%;
      overflow: hidden;
    }

    ^featureSection {
      flex: 0;
      height: auto;
    }

    ^perFeature {
      display: flex;
      padding-bottom: 10px;
    }

    ^left-arrow {
      width: 3%;
      float: left;
      transform: scaleX(-1);
      display: flex;
      padding-top: 70px;
      /* HOVER OFF */
      -webkit-transition: padding 2s;
    }

    ^right-arrow {
      width: 3%;
      float: right;
      display: flex;
      padding-top: 70px;
      margin-left: -20px;
      z-index: 10000;
      position: relative;
      /* HOVER OFF */
      -webkit-transition: padding 2s;
    }

    ^right-arrow:hover {
      transform: scale(1.2);
      /* HOVER ON */
      -webkit-transition: border-radius 2s;
    }

    ^left-arrow:hover {
      transform: scaleX(-1) scale(1.2);
      /* HOVER ON */
      -webkit-transition: border-radius 2s;
    }

    ^container {
      display: inline-block;
      width: 100%;
      height: fit-content;
      overflow-y: visible;
    }
  `,

  properties: [
    {
      name: 'visibleCapabilityDAO',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO with only visible capabilities.
      `,
      factory: function() {
        return this.capabilityDAO
          .where(this.EQ(this.Capability.VISIBLE, true));
      }
    },
    {
      name: 'featuredCapabilities',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO Property to find capabilities to feature.
      `,
      factory: function() {
        return this.visibleCapabilityDAO
          .where(this.IN('featured', this.Capability.KEYWORDS));
      }
    },
    {
      name: 'visibleCategoryDAO',
      class: 'foam.dao.DAOProperty',
      documentation: `
        DAO Property to find categories that are visible.
      `,
      expression: function(capabilityCategoryDAO) {
        return capabilityCategoryDAO
          .where(this.EQ(this.CapabilityCategory.VISIBLE, true));
      }
    },
    {
      name: 'carouselCounter',
      class: 'Int',
      documentation: 'left and right scroll counter for featureCardArray index'
    },
    {
      name: 'totalNumCards',
      class: 'Int',
      documentation: 'should be equivalent to featureCardArray.length'
    },
    {
      name: 'featureCardArray',
      value: [],
      documentation: 'stores the styling of each featureCapability'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.signingOfficerQuestion();

      var self = this;
      window.cstore = self;

      self
        .addClass(self.myClass())
        .add(self.renderFeatured())
        .add(self.accountAndAccountingCard())
        // NOTE: TEMPORARILY REMOVED
        // .start(self.Tabs)
        //   .start(self.Tab, { label: this.TAB_ALL, selected: true })
        //     .add(self.renderFeatured())
        //     .add(self.accountAndAccountingCard())
        //   .end()
        //   // TODO: replace this .call with a .select once
        //   //       duplication error is fixed
        //   .call(function() {
        //     self.visibleCategoryDAO.select().then(a => {
        //       for ( let i = 0 ; i < a.array.length ; i++ ) {
        //         let category = a.array[i];
        //         let e = self.Tab.create({ label: category.name })
        //           .add(self.renderSection(category));
        //         this.add(e);
        //       }
        //     });
        //   })
        .end();
    },

    function renderFeatured() { // Featured Capabilities in carousel view
      var self = this;
      var spot = self.E();
      return this.E().start()// .style({ 'height': 'fit-content', 'overflow-y': 'visible' })
        .addClass(this.myClass('container'))
        .add(this.slot(function(featuredCapabilities) {
          featuredCapabilities.select().then(result => {
            var arr = result.array;
            self.totalNumCards = arr.length;
            self.featureCardArray = [];
            for ( let i = 0 ; i < self.totalNumCards ; i++ ) { // build featured cards as elements
              self.featureCardArray.push(
                () => self.Element.create().start()
                  .addClass(self.myClass('perFeature'))
                  .start(self.CapabilityFeatureView, { data: arr[i] })
                    .addClass(self.myClass('featureSection'))
                  .end()
                  .on('click', () => {
                    self.crunchController
                      .createWizardSequence(arr[i].id).execute();
                  })
                .end());
            }
            spot.start('span').start('img').addClass(self.myClass('left-arrow'))
                .attr('src', 'images/carouselArrow.svg')
                .on('click', function() {
                  self.carouselCounter--;
                })
              .end().end();
            spot.add(self.slot(
              function(carouselCounter, totalNumCards) {
                var ele = self.E().addClass(self.myClass('feature-column-grid'));
                for ( var k = 0 ; k < totalNumCards ; k++ ) {
                  let cc = carouselCounter % totalNumCards; // this stops any out of bounds indecies
                  let index = ( cc + totalNumCards + k ) % totalNumCards; // this ensures circle indecies
                  ele = ele.add(self.featureCardArray[index].call(self));
                }
                return ele;
              }));
            spot.start('span').start('img').addClass(self.myClass('right-arrow'))
              .attr('src', 'images/carouselArrow.svg')
              .on('click', function() {
                self.carouselCounter++;
              })
            .end().end();
          });
          return spot;
       })).end();
    },

    function accountAndAccountingCard() {
      // Capability Store Section Previews
      var self = this;
      return self.E().style({ 'width': '94%', 'margin': 'auto '})
        .select(self.visibleCategoryDAO$proxy, function(category) {
          var sectionElement = this.E('span');
          var returnElement = this.E()
            .start('h3')
              .add({ data : category, clsInfo : category.cls_.NAME.name, default : category.name })
            .end()
            .add(sectionElement);
          var previewIdsPromise = self.getCategoryDAO_(category.id).limit(6)
            .select().then(arraySink => arraySink.array.map(x => x.targetId) );

          previewIdsPromise.then(capabilityIds => {
            self.visibleCapabilityDAO.where(
              self.IN(self.Capability.ID, capabilityIds)
            ).select().then(result => {
              let arr = result.array;
              let grid = self.Grid.create();
              for ( let i = 0 ; i < arr.length ; i++ ) {
                let cap = arr[i];
                grid = grid
                  .start(self.GUnit, { columns: 4 })
                    .tag(self.CapabilityCardView, { data: cap })
                    .on('click', () => {
                      self.crunchController
                        .createWizardSequence(cap).execute();
                    })
                  .end();
              }
              sectionElement.add(grid);
            });
          });
          return returnElement;
        });
    },

    function renderSection(category) {
      var self = this;
      var sectionElement = this.E();

      // Promise 'p' reports capability IDs that are in this category
      var p = self.getCategoryDAO_(category.id).select(
        self.PROJECTION(self.CapabilityCategoryCapabilityJunction.TARGET_ID));

      // When 'p' resolves, query all matching capabilities
      p.then(arraySink => {
        capabilityIds = arraySink.projection;
        self.visibleCapabilityDAO.where(
          self.IN(self.Capability.ID, capabilityIds)
        ).select().then(result => {
          let arr = result.array;
          let grid = self.Grid.create();
          for ( let i = 0 ; i < arr.length ; i++ ) {
            let cap = arr[i];
            grid = grid
              .start(self.GUnit, { columns: 4 })
                .tag(self.CapabilityCardView, { data: cap })
                .on('click', () => {
                  self.crunchController
                    .createWizardSequence(cap).execute();
                })
              .end();
          }
          sectionElement.add(grid);
        });
      });
      return sectionElement;
    },
    function getCategoryDAO_(categoryId) {
      return this.capabilityCategoryCapabilityJunctionDAO.where(
        this.EQ(
          this.CapabilityCategoryCapabilityJunction.SOURCE_ID,
          categoryId));
    },
    function signingOfficerQuestion() {
      return this.auth.check(this.ctrl.__subContext__, 'businessHasSigningOfficer');
    }
  ]
});
<|endoftext|>