├── .agignore ├── img └── scale.png ├── js ├── vendor │ ├── flat_image_zoom │ │ ├── .gitignore │ │ ├── javascripts │ │ │ ├── images │ │ │ │ ├── layers.png │ │ │ │ ├── layers-2x.png │ │ │ │ ├── marker-icon.png │ │ │ │ ├── marker-shadow.png │ │ │ │ └── marker-icon-2x.png │ │ │ └── jquery.actual.min.js │ │ ├── README.md │ │ ├── package.json │ │ ├── LICENSE.txt │ │ └── Gruntfile.js │ ├── angular-segmentio.js │ ├── Leaflet.fullscreen.min.js │ ├── angular-route.min.js │ ├── masonry-horizontal.js │ ├── imagesloaded.pkgd.min.js │ ├── angular-touch.min.js.map │ └── angular-route.min.js.map ├── directives │ ├── hint.js │ ├── ngPoster.js │ ├── recalculateDrawerStates.js │ ├── videoHandler.js │ ├── share.js │ ├── vcenter.js │ ├── controlPanel.js │ ├── transparentize.js │ ├── note.js │ └── flatmap.js ├── controllers │ ├── notes.js │ ├── goldweights.js │ ├── story.js │ ├── main.js │ └── object.js ├── config.js ├── routes.js ├── overscroll.js ├── app.js ├── services │ └── hintManager.js ├── factories.js └── adapters.js ├── fonts └── fontello │ ├── font │ ├── griot.eot │ ├── griot.ttf │ ├── griot.woff │ └── griot.svg │ ├── LICENSE.txt │ ├── css │ ├── griot-codes.css │ ├── animation.css │ ├── griot-ie7-codes.css │ ├── griot-ie7.css │ └── griot.css │ ├── README.txt │ ├── config.json │ └── demo.html ├── humans.txt ├── .gitignore ├── robots.txt ├── sass ├── all.scss ├── drawerify.scss ├── clusters.scss ├── leaflet.scss ├── utils.scss ├── index.scss ├── story.scss ├── object.scss └── generic.scss ├── _deprecated ├── filters.js ├── onPlay.js └── scroll.js ├── views ├── annotations.html ├── goldweights.html ├── story.html ├── index.html └── object.html ├── server └── email.js ├── README.md ├── package.json ├── LICENSE.md ├── Makefile ├── index.html ├── clusters ├── determine-clusters.js └── clusters.json └── css └── goldweights.css /.agignore: -------------------------------------------------------------------------------- 1 | **bundle.js 2 | css/bundle.css 3 | -------------------------------------------------------------------------------- /img/scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/img/scale.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .sass-cache 3 | build 4 | fonts/* 5 | node_modules 6 | -------------------------------------------------------------------------------- /fonts/fontello/font/griot.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/fonts/fontello/font/griot.eot -------------------------------------------------------------------------------- /fonts/fontello/font/griot.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/fonts/fontello/font/griot.ttf -------------------------------------------------------------------------------- /fonts/fontello/font/griot.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/fonts/fontello/font/griot.woff -------------------------------------------------------------------------------- /humans.txt: -------------------------------------------------------------------------------- 1 | /* TEAM */ 2 | 3 | Name: The Minneapolis Institute of Arts 4 | Site: http://github.com/artsmia 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | fallback/*.json 2 | server/.envrc 3 | node_modules 4 | .DS_Store 5 | .sass-cache 6 | css/style.css.map 7 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | -------------------------------------------------------------------------------- /js/directives/hint.js: -------------------------------------------------------------------------------- 1 | app.directive( 'hint', function( $rootScope ) { 2 | return function( scope, elem, attrs ) { 3 | $rootScope.hintSeen = true; 4 | } 5 | }); -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/js/vendor/flat_image_zoom/javascripts/images/layers.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/js/vendor/flat_image_zoom/javascripts/images/layers-2x.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/js/vendor/flat_image_zoom/javascripts/images/marker-icon.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/js/vendor/flat_image_zoom/javascripts/images/marker-shadow.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsmia/griot/HEAD/js/vendor/flat_image_zoom/javascripts/images/marker-icon-2x.png -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/README.md: -------------------------------------------------------------------------------- 1 | flat_image_zoom 2 | ================ 3 | 4 | Rip the image zooming code from 5 | [walkerart/infolounge_walls](https://github.com/walkerart/infolounge_walls). 6 | -------------------------------------------------------------------------------- /sass/all.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | @import 'generic'; 3 | @import 'index'; 4 | @import 'object'; 5 | @import 'story'; 6 | @import 'leaflet'; 7 | @import 'drawerify'; 8 | @import 'clusters'; 9 | @import 'loading-animation'; 10 | -------------------------------------------------------------------------------- /_deprecated/filters.js: -------------------------------------------------------------------------------- 1 | app.filter('titleCase', function () { 2 | return function (input) { 3 | var words = input.replace('_', ' ').split(' '); 4 | for (var i = 0; i < words.length; i++) { 5 | words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1); 6 | } 7 | return words.join(' '); 8 | } // https://gist.github.com/maruf-nc/5625869 9 | }); -------------------------------------------------------------------------------- /js/controllers/notes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller for notes template. 3 | */ 4 | 5 | app.controller('notesCtrl', ['$scope', '$routeParams', 'notes', 6 | function($scope, $routeParams, wp) { 7 | $scope.id = $routeParams.id 8 | wp().then(function(_wp) { 9 | $scope.notes = _wp.objects[$scope.id].views 10 | $scope.$apply() 11 | }) 12 | } 13 | ]) -------------------------------------------------------------------------------- /views/annotations.html: -------------------------------------------------------------------------------- 1 |
    2 |
  1. 3 |
      4 |
    1. 5 | 6 |
      7 |
    2. 8 |
    9 |
  2. 10 |
11 | -------------------------------------------------------------------------------- /js/directives/ngPoster.js: -------------------------------------------------------------------------------- 1 | app.directive('ngPoster', function() { 2 | return { 3 | priority: 99, // it needs to run after the attributes are interpolated 4 | link: function(scope, element, attr) { 5 | attr.$observe('ngPoster', function(value) { 6 | if (!value) return; 7 | attr.$set('poster', value); 8 | }) 9 | } 10 | } 11 | }) // https://github.com/angular/angular.js/blob/v1.2.0/src/ng/directive/booleanAttrs.js#L86 12 | 13 | -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flat_image_zoom", 3 | "version": "0.1.0", 4 | "devDependencies": { 5 | "grunt": "~0.4.1", 6 | "grunt-contrib-jshint": "~0.4.3", 7 | "grunt-contrib-nodeunit": "~0.1.2", 8 | "grunt-contrib-uglify": "~0.2.0", 9 | "grunt-contrib-concat": "~0.3.0", 10 | "grunt-contrib-qunit": "~0.2.1", 11 | "grunt-contrib-watch": "~0.4.3", 12 | "grunt-sass": "~0.6.0", 13 | "grunt-contrib-compass": "~0.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /js/directives/recalculateDrawerStates.js: -------------------------------------------------------------------------------- 1 | app.directive( 'recalculateDrawerStates', function( $timeout ){ 2 | return { 3 | restrict: 'A', 4 | require: '^drawerify', 5 | link: function( scope, elem, attrs, drawerify ){ 6 | elem.on( 'touchend', function(){ 7 | if( 'vertical' === drawerify.orientation ){ 8 | $timeout( function(){ 9 | drawerify.recalculateCustomStates(); 10 | if( 'open' !== drawerify.activeState ){ 11 | drawerify.to('info'); 12 | } 13 | }, 50 ); 14 | } 15 | }); 16 | } 17 | } 18 | }); -------------------------------------------------------------------------------- /sass/drawerify.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Drawerify styles 3 | */ 4 | .drawerify-handle{ 5 | @include icon( '\e817' ); 6 | @include transition( background-color 100ms ease ); 7 | position:absolute; 8 | display:block; 9 | width:70px; 10 | height:70px; 11 | background-color:rgba(237,237,237,0.6); 12 | 13 | &:before{ 14 | font-size:30px; 15 | margin-top:-15px; 16 | } 17 | 18 | .drawerify-horizontal &:before{ 19 | @include transform( rotate( 90deg ) ); 20 | } 21 | 22 | } 23 | 24 | .drawerify-content{ 25 | position:absolute; 26 | top:0; 27 | right:0; 28 | bottom:0; 29 | left:0; 30 | width:100%; 31 | height:100%; 32 | } -------------------------------------------------------------------------------- /sass/clusters.scss: -------------------------------------------------------------------------------- 1 | li.cluster { 2 | border: 1px solid #999; 3 | text-align: center; 4 | padding: 0.25em; 5 | cursor: pointer; 6 | height: 100%; 7 | min-width: 15em; 8 | position: relative; 9 | } 10 | li.cluster p { 11 | margin: 0.25em 0; 12 | } 13 | li.cluster img { 14 | max-width: 200px; 15 | } 16 | 17 | li.cluster a { 18 | padding: 0 0.5em; 19 | position: absolute; 20 | bottom: 0.5em; 21 | left: 5%; 22 | width: 90% 23 | } 24 | li.cluster:hover a, li.cluster:active a { 25 | border: 1px solid #222; 26 | color: #ededed; 27 | background-color: #222; 28 | } 29 | 30 | .cluster.loading { 31 | @extend %loadingAnimation; 32 | } 33 | -------------------------------------------------------------------------------- /_deprecated/onPlay.js: -------------------------------------------------------------------------------- 1 | app.directive("onPlay", function ($window) { 2 | return function(scope, element, attrs) { 3 | element.on('play pause', function() { 4 | $(this).toggleClass('playing') 5 | }) 6 | element.on('play', function() { 7 | var $this = $(this), _ended 8 | if($this.data('fullscreened') == undefined) { // only force fullscreen once 9 | this.webkitEnterFullscreen() 10 | $this.data('fullscreened', true) 11 | } 12 | // return to the normal screen when video ends 13 | _ended || element.on('ended', function() { 14 | this.webkitExitFullScreen() 15 | }) 16 | }) 17 | } 18 | }) 19 | 20 | -------------------------------------------------------------------------------- /server/email.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var cors = require('cors') 3 | var app = express() 4 | var bodyParser = require('body-parser') 5 | 6 | var send = require('mandrill-send')(process.env.MANDRILL_TOKEN) 7 | app.use(cors()) 8 | app.use(bodyParser()) 9 | 10 | app.route('/:email').all(function(req, res, next) { 11 | console.log('.', req.params, req.body) 12 | var email = req.body 13 | send({ 14 | to: [{email: req.param('email')}], 15 | subject: email.subject, 16 | html: email.body, 17 | from: email.from || 'MIAbot ' 18 | }, function(err){ 19 | if (err) console.error(err); 20 | }) 21 | 22 | res.send(req.params) 23 | }) 24 | 25 | app.listen(33445) 26 | -------------------------------------------------------------------------------- /js/directives/videoHandler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Turn a parent element transparent on touchstart. 3 | */ 4 | 5 | app.directive( 'videoHandler', function(){ 6 | 7 | return function( scope, elem, attrs ){ 8 | 9 | var aspect = $(elem).innerHeight() / $(elem).innerWidth(); 10 | 11 | var resize = function(){ 12 | 13 | var containerWidth = $(elem).closest('.story-video').innerWidth(); 14 | 15 | if( window.outerWidth > 1023 ){ 16 | containerWidth -= 140; 17 | } 18 | 19 | $(elem).css({ 20 | 'width': containerWidth + 'px', 21 | 'height': Math.round( containerWidth * aspect ) + 'px', 22 | 'max-width': '800px' 23 | }); 24 | 25 | } 26 | 27 | resize(); 28 | 29 | $(window).on('resize orientationchange', function(){ 30 | setTimeout( function(){ 31 | resize(); 32 | }, 300 ); 33 | }); 34 | 35 | } 36 | 37 | }); -------------------------------------------------------------------------------- /fonts/fontello/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Font license info 2 | 3 | 4 | ## Typicons 5 | 6 | (c) Stephen Hutchings 2012 7 | 8 | Author: Stephen Hutchings 9 | License: SIL (http://scripts.sil.org/OFL) 10 | Homepage: http://typicons.com/ 11 | 12 | 13 | ## MFG Labs 14 | 15 | Copyright (C) 2012 by Daniel Bruce 16 | 17 | Author: MFG Labs 18 | License: SIL (http://scripts.sil.org/OFL) 19 | Homepage: http://www.mfglabs.com/ 20 | 21 | 22 | ## Entypo 23 | 24 | Copyright (C) 2012 by Daniel Bruce 25 | 26 | Author: Daniel Bruce 27 | License: SIL (http://scripts.sil.org/OFL) 28 | Homepage: http://www.entypo.com 29 | 30 | 31 | ## Font Awesome 32 | 33 | Copyright (C) 2012 by Dave Gandy 34 | 35 | Author: Dave Gandy 36 | License: SIL () 37 | Homepage: http://fortawesome.github.com/Font-Awesome/ 38 | 39 | 40 | -------------------------------------------------------------------------------- /js/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure application. 3 | */ 4 | 5 | app.constant('envConfig', { 6 | 7 | // Location of tile server; used in flatmap directive 8 | tilesaw: '//tiles.dx.artsmia.org/', 9 | tileUrlSubdomain: function(tileUrl) { 10 | return tileUrl.replace('http://0.', 'http://{s}.') 11 | }, 12 | 13 | // Location of content 14 | crashpad: 'http://new.artsmia.org/crashpad/griot/', 15 | 16 | // CDN for Goldweights audio (specific to MIA implementation) 17 | cdn: 'http://cdn.dx.artsmia.org/', 18 | 19 | miaEmailSharingActive: true, 20 | emailServer: 'http://dx.artsmia.org:33445/', 21 | 22 | // Adapters - set to false to use GriotWP for everything. 23 | miaMediaMetaActive: true, 24 | miaMediaMetaSrc: 'http://cdn.dx.artsmia.org/credits.json', 25 | miaObjectMetaActive: true, 26 | miaObjectMetaSrc: "http://caption-search.dx.artsmia.org/id/", 27 | miaThumbnailActive: true, 28 | miaThumbnailSrc: 'http://cdn.dx.artsmia.org/' 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Griot 2 | 3 | Griot is an open-source iPad application that facilitates engagement with a collection of **objects** (artifacts, artwork, graphs, or anything else that can be represented visually) through **annotations** (points of interest on the object itself) and **stories** (related text-based and multimedia content, presented as a series of pages). 4 | 5 | The Griot framework requires three components: 6 | 7 | 1. The Griot software itself (this); 8 | 2. A server for creating and serving tiled images ([tilesaw][]); and 9 | 3. An interface for loading content and bundling it in JSON format 10 | ([GriotWP][]). 11 | 12 | ## Installation 13 | 14 | 1. Deploy Griot to the directory where you would like it to run. 15 | 2. Edit `js/config.js` to point to your source of image tiles (i.e. your implementation of [tilesaw][]) and application content (i.e. your implementation of [GriotWP][]). 16 | 17 | [tilesaw]: https://github.com/artsmia/tilesaw 18 | [GriotWP]: https://github.com/artsmia/GriotWP -------------------------------------------------------------------------------- /js/directives/share.js: -------------------------------------------------------------------------------- 1 | app.directive('share', function(email) { 2 | var template = '
' + 3 | '' + 4 | '' + 5 | '
' 6 | 7 | return { 8 | restrict: 'A', 9 | template: template, 10 | link: function(scope, element, attrs) { 11 | scope.showEmail = false 12 | scope.el = element 13 | var emailI = scope.el.find('input')[0] 14 | 15 | scope.toggleEmail = function(e) { 16 | if((e.toElement || e.target).nodeName == 'A') scope.showEmail = !scope.showEmail 17 | emailI.focus() 18 | } 19 | 20 | scope.sendEmail = function() { 21 | email.share(scope.email, {subject: scope.wp.title, body: window.location.href}) 22 | scope.email = '' 23 | scope.showEmail = false 24 | emailI.blur() 25 | } 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "griot", 3 | "version": "0.0.0", 4 | "description": "Griot is an open-source iPad application that facilitates engagement with a collection of **objects** (artifacts, artwork, graphs, or anything else that can be represented visually) through **annotations** (points of interest on the object itself) and **stories** (related text-based and multimedia content, presented as a series of pages).", 5 | "main": "js/app.js", 6 | "dependencies": { 7 | "fastclick": "^1.0.6" 8 | }, 9 | "devDependencies": { 10 | "onchange": "^0.2.0", 11 | "uglify-js": "^2.4.16", 12 | "browserify": "^8.1.3" 13 | }, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/artsmia/griot.git" 20 | }, 21 | "author": "", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/artsmia/griot/issues" 25 | }, 26 | "homepage": "https://github.com/artsmia/griot" 27 | } 28 | -------------------------------------------------------------------------------- /js/directives/vcenter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Vertically centers an element within a container. Apply 'vcenter' class to 3 | * element to be centered and make sure parent is positioned. 4 | */ 5 | 6 | app.directive( 'vcenter', function(){ 7 | 8 | return{ 9 | restrict: 'C', 10 | transclude: true, 11 | template: "
", 12 | link: function( scope, elem, attrs ) { 13 | 14 | scope.container = jQuery( elem ); 15 | 16 | if( scope.container.find( 'video' ).length ) { 17 | 18 | var unwatch = scope.$watch( 19 | function(){ 20 | return scope.container.height(); 21 | }, 22 | function(){ 23 | setTimeout( function(){ 24 | // Use post-transition height (not the one returned by $watch) 25 | var finalHeight = scope.container.height(); 26 | scope.container.find( '.vcenter-cell' ).children('video').css( 'max-height', finalHeight ); 27 | }, 150 ); 28 | } 29 | ); 30 | 31 | scope.$on("$destroy", function(){ 32 | unwatch(); 33 | }); 34 | 35 | } 36 | } 37 | } 38 | 39 | }); -------------------------------------------------------------------------------- /sass/leaflet.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Overrides for default Leaflet control-button styles. 3 | */ 4 | 5 | .leaflet-top .leaflet-bar{ 6 | @include box-shadow(none); 7 | } 8 | 9 | .leaflet-bar a, 10 | .leaflet-bar a:first-child, 11 | .leaflet-bar a:last-child{ 12 | @include transition( all 125ms ease ); 13 | width:30px; 14 | height:30px; 15 | margin:0; 16 | line-height:26px; 17 | vertical-align:middle; 18 | background-color:rgba(22, 22, 22, .55); 19 | color:#fff; 20 | border:2px solid #fff; 21 | text-indent:-60px; 22 | overflow:hidden; 23 | position:relative; 24 | } 25 | .leaflet-bar a:last-child{ 26 | border-width: 0 2px 2px 2px; 27 | } 28 | 29 | .leaflet-bar a.leaflet-control-zoom-in{ 30 | @include icon('\e815'); 31 | border-top-left-radius:1000px; 32 | border-top-right-radius:1000px; 33 | &:before{ 34 | font-size:16px; 35 | margin-top:-8px; 36 | text-indent:0; 37 | } 38 | } 39 | 40 | .leaflet-bar a.leaflet-control-zoom-out{ 41 | @include icon('\e816'); 42 | border-bottom-left-radius:1000px; 43 | border-bottom-right-radius:1000px; 44 | &:before{ 45 | font-size:16px; 46 | margin-top:-8px; 47 | text-indent:0; 48 | } 49 | } -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Walker Art Center 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 the Minneapolis Institute of Arts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/javascripts/jquery.actual.min.js: -------------------------------------------------------------------------------- 1 | /*! Copyright 2012, Ben Lin (http://dreamerslab.com/) 2 | * Licensed under the MIT License (LICENSE.txt). 3 | * 4 | * Version: 1.0.14 5 | * 6 | * Requires: jQuery 1.2.3 ~ 1.9.0 7 | */ 8 | ;(function(e){e.fn.extend({actual:function(t,n){if(!this[t]){throw'$.actual => The jQuery method "'+t+'" you called does not exist'}var r={absolute:false,clone:false,includeMargin:false};var i=e.extend(r,n);var s=this.eq(0);var o,u;if(i.clone===true){o=function(){var e="position: absolute !important; top: -1000 !important; ";s=s.clone().attr("style",e).appendTo("body")};u=function(){s.remove()}}else{var a=[];var f="";var l;o=function(){if(e.fn.jquery>="1.8.0")l=s.parents().addBack().filter(":hidden");else l=s.parents().andSelf().filter(":hidden");f+="visibility: hidden !important; display: block !important; ";if(i.absolute===true)f+="position: absolute !important; ";l.each(function(){var t=e(this);a.push(t.attr("style"));t.attr("style",f)})};u=function(){l.each(function(t){var n=e(this);var r=a[t];if(r===undefined){n.removeAttr("style")}else{n.attr("style",r)}})}}o();var c=/(outer)/g.test(t)?s[t](i.includeMargin):s[t]();u();return c}})})(jQuery) -------------------------------------------------------------------------------- /js/directives/controlPanel.js: -------------------------------------------------------------------------------- 1 | app.directive('controlPanel', function() { 2 | return { 3 | restrict: 'E', 4 | replace: true, 5 | controller: function($scope, $element, $attrs) { 6 | console.log('controlPanel', $scope, $element, $attrs) 7 | }, 8 | template: '
  • '+ 9 | '
    '+ 10 | '
    '+ 11 | '

    Showing objects near Gallery {{gallery}}

    '+ 12 | ''+ 13 | '
    '+ 14 | '
    '+ 15 | '

    Showing Museum Highlights

    '+ 16 | '
    '+ 17 | 'Explore more ArtStories'+ 18 | '
    '+ 19 | '
    '+ 20 | '

    Showing all ArtStories

    '+ 21 | ''+ 22 | 'View only {{isGallery ? "objects nearby" : "Museum Highlights"}}'+ 23 | '
    '+ 24 | '
  • ', 25 | } 26 | }) 27 | 28 | -------------------------------------------------------------------------------- /js/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Application routing 3 | */ 4 | 5 | app.config(['$routeProvider', function($routeProvider) { 6 | return $routeProvider.when('/', { 7 | templateUrl: 'views/index.html', 8 | controller: 'mainCtrl', 9 | resolve: { resolvedNotes: function(notes) { return notes() } } 10 | }).when('/clusters/:cluster', { // TODO: can I de-dupe this in angular? `when('/', '/clusters…')` 11 | templateUrl: 'views/index.html', 12 | controller: 'mainCtrl', 13 | resolve: { resolvedNotes: function(notes) { return notes() } } 14 | }).when('/o/:id', { 15 | templateUrl: 'views/object.html', 16 | controller: 'ObjectCtrl', 17 | resolve: { 18 | resolvedNotes: function(notes) { return notes() }, 19 | resolvedObjectMeta: function(miaObjectMetaAdapter, $route) { 20 | return miaObjectMetaAdapter.get($route.current.params.id) 21 | } 22 | } 23 | }).when('/a/:id', { 24 | templateUrl: 'views/annotations.html', 25 | controller: 'notesCtrl' 26 | }).when('/stories/:id', { 27 | templateUrl: 'views/story.html', 28 | controller: 'storyCtrl' 29 | }).when('/goldweights', { 30 | templateUrl: 'views/goldweights.html', 31 | controller: 'goldweightsCtrl' 32 | }).otherwise({ 33 | redirectTo: '/' 34 | }) 35 | }]) 36 | 37 | -------------------------------------------------------------------------------- /_deprecated/scroll.js: -------------------------------------------------------------------------------- 1 | var _requestAF = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame, 2 | _cancelAF = window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame 3 | 4 | app.directive("scroll", function ($window) { 5 | return function(scope, element, attrs) { 6 | var e = document.querySelector('#info') 7 | scope._scrollCallback = scope.$eval(attrs['scroll']) 8 | var scrollCallback = function(event) { 9 | if(scope.scrollAnimation) _requestAF(scope.scrollAnimation) 10 | scope.scrollAnimation = _cancelAF(function() { 11 | scope.scrolled = e.scrollTop >= 100 12 | scope.pageXOffset = window.pageXOffset 13 | if(scope._scrollCallback) scope._scrollCallback(element) 14 | scope.$$phase || scope.$apply() 15 | }) 16 | }, 17 | hooks = 'touchend touchstart touchmove touchleave touchcancel scroll' 18 | 19 | element.bind(hooks, scrollCallback) 20 | if(!scope._scrollCallback) return 21 | // Bug? binding to element doesn't catch horizontal scrolls… 22 | // use window.addEventListener to cover that. 23 | Array.prototype.map.call(hooks.split(' '), function(hook) { 24 | window.addEventListener(hook, scrollCallback) 25 | }) 26 | } 27 | }) 28 | 29 | -------------------------------------------------------------------------------- /js/overscroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prevent overscroll on iOS. Adapted from 3 | * http://stackoverflow.com/questions/10238084 4 | */ 5 | 6 | var selScrollable = '.scrollable'; 7 | 8 | $(document).on('touchmove',function(e){ 9 | e.preventDefault(); 10 | }); 11 | 12 | $('body').on('touchstart', selScrollable, function(e) { 13 | 14 | if (e.currentTarget.scrollTop === 0) { 15 | e.currentTarget.scrollTop = 1; 16 | } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) { 17 | e.currentTarget.scrollTop -= 1; 18 | } 19 | 20 | if( e.currentTarget.scrollLeft === 0) { 21 | e.currentTarget.scrollLeft = 1; 22 | } else if ( e.currentTarget.scrollWidth === e.currentTarget.scrollLeft + e.currentTarget.offsetWidth ) { 23 | e.currentTarget.scrollLeft -= 1; 24 | } 25 | }); 26 | 27 | $( 'body' ).on( 'touchmove', selScrollable, function( e ){ 28 | // Only block default if internal div contents are large enough to scroll 29 | // Warning: scrollHeight support is not universal. (http://stackoverflow.com/a/15033226/40352) 30 | if( $(this)[0].scrollHeight > $(this).innerHeight() ){ 31 | e.stopPropagation(); 32 | } 33 | 34 | if( $(this)[0].scrollWidth > $(this).innerWidth() ){ 35 | e.stopPropagation(); 36 | } 37 | }); 38 | 39 | $(window).on('orientationchange', function() { 40 | window.scrollTo(0, 0); 41 | }); -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: sass browserify 2 | 3 | .PHONY: sass 4 | sass: 5 | onchange css/style.css -- sh -c 'cat fonts/fontello/css/griot-em* css/vendor/leaflet.css css/style.css css/goldweights.css > css/bundle.css' & 6 | sass --watch -t compact sass/all.scss:css/style.css 7 | 8 | browserify: 9 | browserify --full-path=false js/app.js | uglifyjs > js/bundle.js 10 | 11 | build: browserify 12 | ls -S js/vendor/{angular*,isotope*,masonry*}.js | xargs cat | uglifyjs > js/deps.js 13 | cat js/vendor/jquery-2.1.0.min.js js/vendor/flat_image_zoom/javascripts/{leaflet-src.js,flat_image_zoom.js,jquery.actual.min.js} js/overscroll.js | uglifyjs > js/zooming.js 14 | cat fonts/fontello/css/griot-em* css/vendor/leaflet.css css/style.css css/goldweights.css > css/bundle.css 15 | 16 | watchify: 17 | watchify --full-path=false js/app.js -v -o js/bundle.js 18 | 19 | cdnify: 20 | curl -L http://new.artsmia.org/crashpad/json > fallback/crashpad.json 21 | scp fallback/crashpad.json dx:/apps/cdn/crashpad.json 22 | scp fallback/crashpad.json dxt:/apps/cdn/crashpad.json 23 | 24 | images_used: fallback/crashpad.json 25 | cat fallback/crashpad.json | jq '.objects[].views[].image, .stories[].pages[].image, .stories[].pages[].imageB' | uniq | tr -d null | tr -s '\n' | tr -d \" 26 | 27 | all_goldweights_ids: fallback/crashpad.json 28 | cat fallback/crashpad.json | jq '.objects["196"].views[].annotations[].attachments' | json -g | jq '.[]' | sed 's/"//g' | tr -s ' ' | cut -d' ' -f2 29 | -------------------------------------------------------------------------------- /fonts/fontello/css/griot-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-resize-full:before { content: '\e800'; } /* '' */ 3 | .icon-resize-normal:before { content: '\e801'; } /* '' */ 4 | .icon-home:before { content: '\e802'; } /* '' */ 5 | .icon-menu:before { content: '\e803'; } /* '' */ 6 | .icon-left-open:before { content: '\e804'; } /* '' */ 7 | .icon-right-open:before { content: '\e805'; } /* '' */ 8 | .icon-left:before { content: '\e806'; } /* '' */ 9 | .icon-right:before { content: '\e807'; } /* '' */ 10 | .icon-left-small:before { content: '\e808'; } /* '' */ 11 | .icon-right-small:before { content: '\e809'; } /* '' */ 12 | .icon-play:before { content: '\e80a'; } /* '' */ 13 | .icon-info-circled:before { content: '\e80b'; } /* '' */ 14 | .icon-info-circled-alt:before { content: '\e80c'; } /* '' */ 15 | .icon-cancel-circled:before { content: '\e80d'; } /* '' */ 16 | .icon-cancel-circled-outline:before { content: '\e80e'; } /* '' */ 17 | .icon-cancel:before { content: '\e80f'; } /* '' */ 18 | .icon-cancel-outline:before { content: '\e810'; } /* '' */ 19 | .icon-pause:before { content: '\e811'; } /* '' */ 20 | .icon-back:before { content: '\e812'; } /* '' */ 21 | .icon-info:before { content: '\e813'; } /* '' */ 22 | .icon-doc-text:before { content: '\e814'; } /* '' */ 23 | .icon-plus:before { content: '\e815'; } /* '' */ 24 | .icon-minus:before { content: '\e816'; } /* '' */ 25 | .icon-resize-vertical:before { content: '\e817'; } /* '' */ 26 | .icon-mail:before { content: '\e818'; } /* '' */ -------------------------------------------------------------------------------- /js/directives/transparentize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Turn a parent element transparent on touchstart. 3 | */ 4 | 5 | app.directive( 'transparentize', function($timeout){ 6 | 7 | return { 8 | restrict:'A', 9 | require:'^?drawerify', 10 | link: function( scope, elem, attrs, drawerify ) { 11 | 12 | var $target = jQuery( attrs.transparentize ); 13 | 14 | elem.on( 'touchstart mousedown', function(){ 15 | if( attrs.hasOwnProperty( 'transparentizeAction' ) ){ 16 | switch( attrs.transparentizeAction ){ 17 | 18 | case 'playVideo': 19 | // Close drawer in case we're not on a device that automatically 20 | // full-screens the video 21 | if( drawerify ){ 22 | drawerify.to('closed'); 23 | $timeout( function(){ 24 | var $video = $('video[src="' + scope.page.video + '"]'); 25 | $video[0].play(); 26 | }, 150 ); 27 | } 28 | else { 29 | var $video = $('video[src="' + scope.page.video + '"]'); 30 | $video[0].play(); 31 | } 32 | break; 33 | 34 | 35 | default: 36 | $target.addClass('transparentized'); 37 | } 38 | } 39 | else { 40 | $target.addClass('transparentized'); 41 | } 42 | }); 43 | 44 | elem.on( 'touchend mouseup', function(e){ 45 | $target.addClass('detransparentized'); 46 | $target.removeClass('transparentized'); 47 | $timeout(function() { 48 | $target.removeClass('detransparentized'); 49 | }, 300) 50 | }); 51 | 52 | } 53 | } 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /views/goldweights.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 |
    5 | 6 | 7 |
    8 |
    9 |
    10 | 11 |
      12 |
    1. 13 |

      14 |
      15 |

      16 | 17 |

      18 | 19 |
        20 |
      • 21 | 22 | 23 | 24 | 29 |
      • 30 |
      31 |
    2. 32 |
    3. Goldweights

    4. 33 |
    34 |
    35 | 36 |
    37 | 38 |

    Tap on a goldweight to learn more.

    39 |

    Proverbs spoken by Fred Adiyia.

    40 |
    41 |
    42 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set up application and load modules. 3 | */ 4 | var fastclick = require('fastclick') 5 | 6 | /*jshint asi: true*/ 7 | 'use strict'; 8 | 9 | window.app = angular.module('griot', ['ngRoute', 'segmentio']); 10 | 11 | app.run(function() { fastclick(document.body) }) 12 | 13 | require('./routes') 14 | 15 | require('./services/hintManager') 16 | 17 | require('./config') 18 | 19 | app.config( 20 | ['$httpProvider', function($httpProvider) { 21 | return delete $httpProvider.defaults.headers.common['X-Requested-With']; 22 | }] 23 | ) 24 | 25 | app.run(['$rootScope', 'envConfig', 'miaMediaMetaAdapter', 'miaObjectMetaAdapter', 'miaThumbnailAdapter', '$location', 'hintManager', function( root, config, mediaMeta, objectMeta, objectThumb, $location, hintManager ) { 26 | root.cdn = config.cdn; 27 | var query = $location.search(); 28 | 29 | // If adapters are enabled, retrieve and prepare alternate data 30 | if( config.miaMediaMetaActive ) { 31 | mediaMeta.build( config.miaMediaMetaSrc ); 32 | } 33 | if( config.miaThumbnailActive ) { 34 | objectThumb.init( config.miaThumbnailSrc ); 35 | } 36 | 37 | // hintManager.init(); 38 | // Temporarily disable 'pinch to zoom' hints. 39 | 40 | }]) 41 | 42 | require('./factories') 43 | require('./adapters') 44 | 45 | require('./controllers/object') 46 | require('./controllers/story') 47 | require('./controllers/notes') 48 | require('./controllers/main') 49 | require('./controllers/goldweights') 50 | 51 | require('./directives/flatmap') 52 | require('./directives/note') 53 | require('./directives/vcenter') 54 | require('./directives/ngPoster') 55 | require('./directives/transparentize') 56 | require('./directives/drawerify') 57 | require('./directives/recalculateDrawerStates') 58 | require('./directives/share') 59 | require('./directives/videoHandler') 60 | require('./directives/hint') 61 | require('./directives/controlPanel') 62 | -------------------------------------------------------------------------------- /js/services/hintManager.js: -------------------------------------------------------------------------------- 1 | app.service( 'hintManager', function( $location, $timeout, $rootScope ) { 2 | 3 | var _this = this; 4 | 5 | // Wait this number of seconds after last touch before displaying hints again 6 | this.delay = 60; 7 | 8 | // Has a hint been seen by the user yet? 9 | $rootScope.hintSeen = false; 10 | 11 | this.init = function(){ 12 | 13 | // Start with hints off 14 | $rootScope.showHints = false; 15 | 16 | // Has the user seen a hint yet? 17 | $rootScope.hintSeen = false; 18 | 19 | var query = $location.search(); 20 | 21 | // Directs app to refresh hints after a minute of inactivity. 22 | $rootScope.hosted = query.hasOwnProperty( 'hosted' ) && query.hosted === 'true'; 23 | 24 | // Forces app to assume browser has touch events enabled. 25 | if( query.hasOwnProperty( 'touch' ) && query.touch === 'true' ){ 26 | this.setTouch(); 27 | } else { 28 | $rootScope.touch = false; 29 | } 30 | 31 | // If we aren't explicitly told that this screen is touchable, listen for 32 | // a touch event and activate hints if one is heard. 33 | if( ! $rootScope.touch ){ 34 | $(window).on( 'touchstart', _this.setTouch ); 35 | } 36 | } 37 | 38 | // Sets touch to true, and removes the listener on window if applicable 39 | this.setTouch = function(){ 40 | $timeout( function(){ 41 | $rootScope.touch = true; 42 | $rootScope.showHints = true; 43 | $(window).off( 'touchstart', _this.setTouch ); 44 | if( $rootScope.hosted ){ 45 | $(window).on( 'touchend', function(){ 46 | _this.resetTimer( _this.delay ); 47 | }); 48 | } 49 | }); 50 | } 51 | 52 | this.resetTimer = function( delay ){ 53 | 54 | // Clear timer if it exists 55 | if( _this.hasOwnProperty( 'hintTimer' ) ){ 56 | $timeout.cancel( _this.hintTimer ); 57 | } 58 | // Set new timer 59 | _this.hintTimer = $timeout( function(){ 60 | if( $rootScope.touch ){ 61 | $rootScope.showHints = true; 62 | } 63 | }, delay * 1000 ); 64 | } 65 | 66 | $rootScope.$on( 'zoom', function(){ 67 | if( $rootScope.hintSeen ){ 68 | $timeout( function(){ 69 | $rootScope.showHints = false; 70 | }); 71 | } 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /fonts/fontello/css/animation.css: -------------------------------------------------------------------------------- 1 | /* 2 | Animation example, for spinners 3 | */ 4 | .animate-spin { 5 | -moz-animation: spin 2s infinite linear; 6 | -o-animation: spin 2s infinite linear; 7 | -webkit-animation: spin 2s infinite linear; 8 | animation: spin 2s infinite linear; 9 | display: inline-block; 10 | } 11 | @-moz-keyframes spin { 12 | 0% { 13 | -moz-transform: rotate(0deg); 14 | -o-transform: rotate(0deg); 15 | -webkit-transform: rotate(0deg); 16 | transform: rotate(0deg); 17 | } 18 | 19 | 100% { 20 | -moz-transform: rotate(359deg); 21 | -o-transform: rotate(359deg); 22 | -webkit-transform: rotate(359deg); 23 | transform: rotate(359deg); 24 | } 25 | } 26 | @-webkit-keyframes spin { 27 | 0% { 28 | -moz-transform: rotate(0deg); 29 | -o-transform: rotate(0deg); 30 | -webkit-transform: rotate(0deg); 31 | transform: rotate(0deg); 32 | } 33 | 34 | 100% { 35 | -moz-transform: rotate(359deg); 36 | -o-transform: rotate(359deg); 37 | -webkit-transform: rotate(359deg); 38 | transform: rotate(359deg); 39 | } 40 | } 41 | @-o-keyframes spin { 42 | 0% { 43 | -moz-transform: rotate(0deg); 44 | -o-transform: rotate(0deg); 45 | -webkit-transform: rotate(0deg); 46 | transform: rotate(0deg); 47 | } 48 | 49 | 100% { 50 | -moz-transform: rotate(359deg); 51 | -o-transform: rotate(359deg); 52 | -webkit-transform: rotate(359deg); 53 | transform: rotate(359deg); 54 | } 55 | } 56 | @-ms-keyframes spin { 57 | 0% { 58 | -moz-transform: rotate(0deg); 59 | -o-transform: rotate(0deg); 60 | -webkit-transform: rotate(0deg); 61 | transform: rotate(0deg); 62 | } 63 | 64 | 100% { 65 | -moz-transform: rotate(359deg); 66 | -o-transform: rotate(359deg); 67 | -webkit-transform: rotate(359deg); 68 | transform: rotate(359deg); 69 | } 70 | } 71 | @keyframes spin { 72 | 0% { 73 | -moz-transform: rotate(0deg); 74 | -o-transform: rotate(0deg); 75 | -webkit-transform: rotate(0deg); 76 | transform: rotate(0deg); 77 | } 78 | 79 | 100% { 80 | -moz-transform: rotate(359deg); 81 | -o-transform: rotate(359deg); 82 | -webkit-transform: rotate(359deg); 83 | transform: rotate(359deg); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /js/vendor/flat_image_zoom/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | 7 | concat: { 8 | options: { 9 | // define a string to put between each file in the concatenated output 10 | separator: ';' 11 | }, 12 | dist: { 13 | // the files to concatenate 14 | //src: ['javascripts/*.js'], 15 | 16 | src: [ 17 | 'javascripts/jquery-2.0.1.min.js', 18 | 'javascripts/underscore-min.js', 19 | 'javascripts/leaflet-src.js', 20 | 'javascripts/zoom_swipe.js', 21 | 'javascripts/jquery.actual.min.js', 22 | 'javascripts/flat_image_zoom.js', 23 | ], 24 | // the location of the resulting JS file 25 | dest: 'build/<%= pkg.name %>.js' 26 | } 27 | }, 28 | 29 | jshint: { 30 | options: { 31 | curly: true, 32 | eqeqeq: true, 33 | eqnull: true, 34 | browser: true, 35 | globals: { 36 | jQuery: true 37 | }, 38 | }, 39 | 40 | files: { 41 | src: [ ] 42 | } 43 | }, 44 | 45 | uglify: { 46 | options: { 47 | // the banner is inserted at the top of the output 48 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n', 49 | //report : 'gzip', 50 | // to help optimize the varibale names and stuff like that 51 | //compress : true 52 | }, 53 | dist: { 54 | files: { 55 | 'build/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>'] 56 | } 57 | } 58 | }, 59 | 60 | watch: { 61 | files: ['javascripts/*.js'], 62 | tasks: ['concat', 'uglify'], 63 | } 64 | 65 | }); 66 | 67 | grunt.loadNpmTasks('grunt-contrib-uglify'); 68 | grunt.loadNpmTasks('grunt-contrib-concat'); 69 | grunt.loadNpmTasks('grunt-contrib-jshint'); 70 | grunt.loadNpmTasks('grunt-contrib-qunit'); 71 | grunt.loadNpmTasks('grunt-contrib-watch'); 72 | grunt.loadNpmTasks('grunt-sass'); 73 | grunt.loadNpmTasks('grunt-contrib-compass'); 74 | 75 | // Default task(s). 76 | grunt.registerTask('default', ['concat', 'uglify']); 77 | }; 78 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | MIA ArtStories 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 |
    25 | 26 | 27 | 28 | 29 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /sass/utils.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Media queries, vendor prefixes, and base styles for frequently used elements. 3 | */ 4 | 5 | $smallPortrait:'screen and (orientation:portrait) and (max-width:640px)'; 6 | $small:'screen and (min-width:641px), screen and (orientation:landscape)'; 7 | $medium:'screen and (min-width:1024px)'; 8 | 9 | @mixin box-sizing( $params ) { 10 | -webkit-box-sizing:$params; 11 | -moz-box-sizing:$params; 12 | box-sizing:$params; 13 | } 14 | @mixin box-shadow( $params... ) { 15 | -webkit-box-shadow:$params; 16 | -moz-box-shadow:$params; 17 | box-shadow:$params; 18 | } 19 | @mixin transition( $params... ) { 20 | -webkit-transition: $params; 21 | -moz-transition: $params; 22 | -o-transition: $params; 23 | } 24 | @mixin transform( $params ) { 25 | -webkit-transform:$params; 26 | -moz-transform:$params; 27 | -o-transform:$params; 28 | -ms-transform:$params; 29 | transform:$params; 30 | } 31 | @mixin transform-origin( $params ) { 32 | -webkit-transform-origin:$params; 33 | -moz-transform-origin:$params; 34 | -o-transform-origin:$params; 35 | -ms-transform-origin:$params; 36 | transform-origin:$params; 37 | } 38 | @mixin no-select { 39 | -webkit-touch-callout: none; 40 | -webkit-user-select: none; 41 | -khtml-user-select: none; 42 | -moz-user-select: none; 43 | -ms-user-select: none; 44 | user-select: none; 45 | } 46 | @mixin label{ 47 | color:#222; 48 | font-size:0.8em; 49 | font-family:'MiaGrotesk-Light', sans-serif; 50 | text-align:center; 51 | text-transform:uppercase; 52 | } 53 | @mixin meta{ 54 | color:#888; 55 | font-size:0.9em; 56 | font-family:'MiaGrotesk-Light', sans-serif; 57 | font-style:italic; 58 | } 59 | @mixin icon( $content ){ 60 | font-style:normal; 61 | cursor:pointer; 62 | -webkit-tap-highlight-color: transparent; 63 | 64 | &:before{ 65 | position:absolute; 66 | display:block; 67 | top:50%; 68 | width:100%; 69 | text-align:center; 70 | line-height:100%; 71 | font-family:'griot'; 72 | content: $content; 73 | 74 | // -- rev -- // 75 | //font-size:24px; 76 | //margin-top:-12px; 77 | left:0; 78 | } 79 | } 80 | @mixin truncate { 81 | width: 100%; 82 | white-space: nowrap; 83 | overflow: hidden; 84 | text-overflow: ellipsis; 85 | } 86 | .transparentized{ 87 | @include transition( opacity 300ms ease-out ); 88 | opacity: 0 !important; 89 | } 90 | .detransparentized { 91 | @include transition( opacity 150ms ease-in ); 92 | } 93 | -------------------------------------------------------------------------------- /js/vendor/angular-segmentio.js: -------------------------------------------------------------------------------- 1 | angular.module('segmentio', ['ng']) 2 | .factory('segmentio', ['$rootScope', '$window', '$location', '$log', function ($rootScope, $window, $location, $log) { 3 | var service = {}; 4 | 5 | $window.analytics = $window.analytics || []; 6 | 7 | // Define a factory that generates wrapper methods to push arrays of 8 | // arguments onto our `analytics` queue, where the first element of the arrays 9 | // is always the name of the analytics.js method itself (eg. `track`). 10 | var methodFactory = function (type) { 11 | return function () { 12 | var args = Array.prototype.slice.call(arguments, 0); 13 | if(analytics.initialized) { 14 | $window.analytics[type].apply($window.analytics, args); 15 | } 16 | else { 17 | $window.analytics.push([type].concat(args)); 18 | } 19 | }; 20 | }; 21 | 22 | // Loop through analytics.js' methods and generate a wrapper method for each. 23 | var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick', 24 | 'trackSubmit', 'pageview', 'ab', 'alias', 'ready', 'group']; 25 | for (var i = 0; i < methods.length; i++) { 26 | service[methods[i]] = methodFactory(methods[i]); 27 | } 28 | 29 | // Listening to $viewContentLoaded event to track pageview 30 | $rootScope.$on('$viewContentLoaded', function () { 31 | service.pageview($location.path()); 32 | }); 33 | 34 | /** 35 | * @description 36 | * Load Segment.io analytics script 37 | * @param apiKey The key API to use 38 | */ 39 | service.load = function(apiKey) { 40 | // Create an async script element for analytics.js. 41 | var script = document.createElement('script'); 42 | script.type = 'text/javascript'; 43 | script.async = true; 44 | script.src = ('https:' === document.location.protocol ? 'https://' : 'http://') + 45 | 'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + apiKey + '/analytics.js'; 46 | 47 | // Find the first script element on the page and insert our script next to it. 48 | var firstScript = document.getElementsByTagName('script')[0]; 49 | firstScript.parentNode.insertBefore(script, firstScript); 50 | }; 51 | 52 | return service; 53 | }]); -------------------------------------------------------------------------------- /clusters/determine-clusters.js: -------------------------------------------------------------------------------- 1 | // http://caption-search.dx.artsmia.org/ids/45269,4866,1312,108767,113136,111099,97,111879,1854,12111,1937,3778,115514,115320,1358,111088,114833,111893,12092,376,105014,117153,3520,91467,7505,5788,4324,114429,118304,116725,108860,60728,4379,1380,4829,22412,107241,116294,113926,1704,1721,2210,2606,537,1218,1978,4418,10436,529,109122,278,1270,1348,1727,1355,115836,1411,1244,1748,8023,1226,119599,115352,113568,1637,1629,116116,98653,114514,109112,43877 2 | var objects = require("./objects.json") 3 | var locations = "G200 G219 G223 G230 G258 G260 G261 G321 G340 G350 G363 G374 G375 G379 G236 G250 G254".split(' ') 4 | 5 | objectLocations = objects.map(function(o) { return o.room }) 6 | 7 | // console.log('cube locations', locations) 8 | // console.log('object locations', objectLocations) 9 | 10 | var groups = {} 11 | var id = function(o) { return o.id } 12 | 13 | // How to group the artstories?? 14 | // Location-wise I want to do it by the gallery number. 15 | // So first, 'Not on View' 16 | 17 | groups.offView = objects.filter(function(o) { return o.room == 'Not on View' }).map(id) 18 | 19 | // The simplest possible grouping now is by floor… 20 | 21 | groups.floor1 = objects.filter(function(o) { return o.room.match(/^G1/) }).map(id) 22 | groups.floor2 = objects.filter(function(o) { return o.room.match(/^G2/) }).map(id) 23 | groups.floor3 = objects.filter(function(o) { return o.room.match(/^G3/) }).map(id) 24 | 25 | // I want to group them according to the closest gallery-located iPads 26 | // `locations` 27 | 28 | function galleryDistance(a, b) { 29 | if(a == 'Not on View' || b == 'Not on View') return 1000 30 | a = parseInt(a.replace('G', '')) 31 | b = parseInt(b.replace('G', '')) 32 | return Math.abs(a-b); 33 | } 34 | 35 | function findClusters(object, threshold) { 36 | var objectLocation = object.room 37 | // How far is it to the existing clusters? 38 | var clusterDistances = locations.map(function(clusterLocation) { 39 | return galleryDistance(objectLocation, clusterLocation) 40 | }) 41 | return clusterDistances.map(function(d, i) { 42 | if(d <= threshold) return locations[i] 43 | }).filter(function(cluster) { return cluster }) 44 | } 45 | 46 | var clusters = locations.reduce(function(clusters, location) { 47 | clusters[location] = [] 48 | return clusters 49 | }, {}) 50 | 51 | // find the 'nearest' clusters for each object 52 | objects.map(function(o) { 53 | findClusters(o, 25).map(function(cluster) { 54 | clusters[cluster].push(o.id) 55 | }) 56 | }) 57 | 58 | // TODO: sort objects in clusters by lowest distance 59 | 60 | clusters.offView = groups.offView 61 | 62 | console.log(JSON.stringify(clusters)) 63 | -------------------------------------------------------------------------------- /fonts/fontello/css/griot-ie7-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-resize-full { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 3 | .icon-resize-normal { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 4 | .icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 5 | .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 6 | .icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 7 | .icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 8 | .icon-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 9 | .icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 10 | .icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 11 | .icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 12 | .icon-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 13 | .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 14 | .icon-info-circled-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 15 | .icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 16 | .icon-cancel-circled-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 17 | .icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 18 | .icon-cancel-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 19 | .icon-pause { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 20 | .icon-back { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 21 | .icon-info { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 22 | .icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 23 | .icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 24 | .icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 25 | .icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 26 | .icon-mail { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /js/vendor/Leaflet.fullscreen.min.js: -------------------------------------------------------------------------------- 1 | L.Control.Fullscreen=L.Control.extend({options:{position:"topleft",title:{"false":"View Fullscreen","true":"Exit Fullscreen"}},onAdd:function(map){var container=L.DomUtil.create("div","leaflet-control-fullscreen leaflet-bar leaflet-control");this.link=L.DomUtil.create("a","leaflet-control-fullscreen-button leaflet-bar-part",container);this.link.href="#";this._map=map;this._map.on("fullscreenchange",this._toggleTitle,this);this._toggleTitle();L.DomEvent.on(this.link,"click",this._click,this);return container},_click:function(e){L.DomEvent.stopPropagation(e);L.DomEvent.preventDefault(e);this._map.toggleFullscreen()},_toggleTitle:function(){this.link.title=this.options.title[this._map.isFullscreen()]}});L.Map.include({isFullscreen:function(){return this._isFullscreen||false},toggleFullscreen:function(){var container=this.getContainer();if(this.isFullscreen()){if(document.exitFullscreen){document.exitFullscreen()}else if(document.mozCancelFullScreen){document.mozCancelFullScreen()}else if(document.webkitCancelFullScreen){document.webkitCancelFullScreen()}else{L.DomUtil.removeClass(container,"leaflet-pseudo-fullscreen");this._toggleFullscreenClass();this.invalidateSize();this._isFullscreen=false;this.fire("fullscreenchange")}}else{if(container.requestFullscreen){container.requestFullscreen()}else if(container.mozRequestFullScreen){container.mozRequestFullScreen()}else if(container.webkitRequestFullscreen){container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)}else{L.DomUtil.addClass(container,"leaflet-pseudo-fullscreen");this._toggleFullscreenClass();this.invalidateSize();this._isFullscreen=true;this.fire("fullscreenchange")}}},_toggleFullscreenClass:function(){var container=this.getContainer();if(this.isFullscreen()){L.DomUtil.removeClass(container,"leaflet-fullscreen-on")}else{L.DomUtil.addClass(container,"leaflet-fullscreen-on")}},_onFullscreenChange:function(){var fullscreenElement=document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement;this._toggleFullscreenClass();if(fullscreenElement===this.getContainer()){this._isFullscreen=true;this.fire("fullscreenchange")}else if(this._isFullscreen){this._isFullscreen=false;this.fire("fullscreenchange")}}});L.Map.mergeOptions({fullscreenControl:false});L.Map.addInitHook(function(){if(this.options.fullscreenControl){this.fullscreenControl=new L.Control.Fullscreen;this.addControl(this.fullscreenControl)}var fullscreenchange;if("onfullscreenchange"in document){fullscreenchange="fullscreenchange"}else if("onmozfullscreenchange"in document){fullscreenchange="mozfullscreenchange"}else if("onwebkitfullscreenchange"in document){fullscreenchange="webkitfullscreenchange"}if(fullscreenchange){this.whenReady(function(){L.DomEvent.on(document,fullscreenchange,this._onFullscreenChange,this)});this.on("unload",function(){L.DomEvent.off(document,fullscreenchange,this._onFullscreenChange)})}});L.control.fullscreen=function(options){return new L.Control.Fullscreen(options)}; -------------------------------------------------------------------------------- /js/controllers/goldweights.js: -------------------------------------------------------------------------------- 1 | app.controller('goldweightsCtrl', ['$scope', '$sce', 'segmentio', 'notes', 'miaObjectMetaAdapter', 'miaThumbnailAdapter', function($scope, $sce, segmentio, wp, objectMeta, objectThumb ) { 2 | wp().then(function(wordpress) { 3 | window.$scope = $scope 4 | Zoomer.windowResized() 5 | $scope.goldweights = wordpress.objects['196'] 6 | $scope.goldweights.trustedDescription = $sce.trustAsHtml( $scope.goldweights.description ); 7 | $scope.showDescription = true 8 | 9 | var loadNotes = function() { 10 | $scope.notes = $scope.goldweights.views 11 | angular.forEach($scope.notes, function(view) { 12 | angular.forEach(view.annotations, function(ann) { 13 | var proverbLinkPattern = /\n?

    \[(PR\d+)\]<\/p>/, match = ann.description.match(proverbLinkPattern), proverbId = match && match[1] 14 | ann.proverb = proverbId 15 | ann.trustedAudio = $sce.trustAsResourceUrl($scope.cdn+'goldweights/'+proverbId+'.mp3') 16 | ann.trustedDescription = $sce.trustAsHtml(ann.description.replace(proverbLinkPattern, '')) 17 | angular.forEach(ann.attachments, function(att) { 18 | var mash = att.image_id.split(' '); 19 | att.image_id = mash[0]; 20 | att.object_id = mash[1]; 21 | att.meta1 = att.metaG = ''; 22 | if( objectMeta.isActive ) { // Let's hope it is, because this data does not exist elsewhere 23 | att.title = objectMeta.get( att.object_id, 'gw_title' ); 24 | att.meta1 = objectMeta.get( att.object_id, 'meta1' ); 25 | att.meta2 = objectMeta.get( att.object_id, 'gw_meta2' ); 26 | } 27 | if( objectThumb.isActive ) { 28 | att.thumb = objectThumb.get( att.image_id ); 29 | } 30 | }) 31 | }) 32 | }) 33 | $scope.$$phase || $scope.$apply() 34 | } 35 | $scope.$on('viewChanged', loadNotes) 36 | if($scope.mapLoaded) loadNotes() 37 | }) 38 | 39 | $scope.play = function(scope, $event) { 40 | $('audio').each(function() { this.pause() }) 41 | var audio = $event.target.querySelector('audio') 42 | audio.paused ? audio.play() : audio.pause() 43 | scope.playing = !audio.paused 44 | audio.addEventListener('ended', function() { 45 | scope.playing = false 46 | scope.$apply() 47 | }) 48 | } 49 | 50 | $scope.toggle = function(scope) { 51 | $scope.popupWeight = ($scope.popupWeight == scope ? undefined : scope) 52 | } 53 | 54 | $scope.toggleInfo = function(scope) { 55 | $scope.showInfo = !$scope.showInfo 56 | } 57 | 58 | if(window.location.href.match(/west/)) { 59 | $('body').addClass('west') 60 | setTimeout(function() { 61 | window.scrollTo(0, document.body.scrollHeight) 62 | }, 1000) 63 | } 64 | 65 | $scope.home = function() { 66 | angular.forEach($scope.notes[0].annotations, function(note) { note.active = false }) 67 | $scope.$$phase || $scope.$apply() 68 | } 69 | 70 | }]) -------------------------------------------------------------------------------- /fonts/fontello/css/griot-ie7.css: -------------------------------------------------------------------------------- 1 | [class^="icon-"], [class*=" icon-"] { 2 | font-family: 'griot'; 3 | font-style: normal; 4 | font-weight: normal; 5 | 6 | /* fix buttons height */ 7 | line-height: 1em; 8 | 9 | /* you can be more comfortable with increased icons size */ 10 | /* font-size: 120%; */ 11 | } 12 | 13 | .icon-resize-full { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 14 | .icon-resize-normal { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 15 | .icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 16 | .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 17 | .icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 18 | .icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 19 | .icon-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 20 | .icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 21 | .icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 22 | .icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 23 | .icon-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 24 | .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 25 | .icon-info-circled-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 26 | .icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 27 | .icon-cancel-circled-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 28 | .icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 29 | .icon-cancel-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 30 | .icon-pause { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 31 | .icon-back { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 32 | .icon-info { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 33 | .icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 34 | .icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 35 | .icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 36 | .icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 37 | .icon-mail { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /fonts/fontello/README.txt: -------------------------------------------------------------------------------- 1 | This webfont is generated by http://fontello.com open source project. 2 | 3 | 4 | ================================================================================ 5 | Please, note, that you should obey original font licences, used to make this 6 | webfont pack. Details available in LICENSE.txt file. 7 | 8 | - Usually, it's enough to publish content of LICENSE.txt file somewhere on your 9 | site in "About" section. 10 | 11 | - If your project is open-source, usually, it will be ok to make LICENSE.txt 12 | file publically available in your repository. 13 | 14 | - Fonts, used in Fontello, don't require to make clickable links on your site. 15 | But any kind of additional authors crediting is welcome. 16 | ================================================================================ 17 | 18 | 19 | Comments on archive content 20 | --------------------------- 21 | 22 | - /font/* - fonts in different formats 23 | 24 | - /css/* - different kinds of css, for all situations. Should be ok with 25 | twitter bootstrap. Also, you can skip style and assign icon classes 26 | directly to text elements, if you don't mind about IE7. 27 | 28 | - demo.html - demo file, to show your webfont content 29 | 30 | - LICENSE.txt - license info about source fonts, used to build your one. 31 | 32 | - config.json - keeps your settings. You can import it back to fontello anytime, 33 | to continue your work 34 | 35 | 36 | Why so many CSS files ? 37 | ----------------------- 38 | 39 | Because we like to fit all your needs :) 40 | 41 | - basic file, .css - is usually enougth, in contains @font-face 42 | and character codes definition 43 | 44 | - *-ie7.css - if you need IE7 support, but still don't wish to put char codes 45 | directly into html 46 | 47 | - *-codes.css and *-ie7-codes.css - if you like to use your own @font-face 48 | rules, but still wish to benefit of css generation. That can be very 49 | convenient for automated assets build systems. When you need to update font - 50 | no needs to manually edit files, just override old version with archive 51 | content. See fontello source codes for example. 52 | 53 | - *-embedded.css - basic css file, but with embedded WOFF font, to avoid 54 | CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain. 55 | We strongly recommend to resolve this issue by `Access-Control-Allow-Origin` 56 | server headers. But if you ok with dirty hack - this file is for you. Note, 57 | that data url moved to separate @font-face to avoid problems with 2 | 3 |

    4 | 5 |
    6 | 7 |
    8 | 9 |
    10 | 11 | 12 |
    13 | 14 | 15 |
    16 | 17 | 18 |
    19 | 20 |
    21 | 22 | 23 |
    24 | 25 |
    26 | 27 | 28 | 29 | 30 |
    31 | 32 |
    37 | 38 |
    39 |

    {{page.glanceText}}

    40 |
    41 | 42 |
    43 | 44 |
    45 | 46 | 47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 |
    2 | 39 |
    40 | 41 | 45 | 46 | 53 | -------------------------------------------------------------------------------- /js/vendor/angular-route.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.2.15 3 | (c) 2010-2014 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){k.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=b;h.$emit("$viewContentLoaded");h.$eval(u)}else y()} 7 | var h,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){}, 8 | {prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;k.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b= 9 | "/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart", 10 | d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=h.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl= 11 | b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(k,function(f,k){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h\[(http:\/\/.*)\]<\/p>/, 24 | match = page.text.match(iframe_pattern) 25 | if(match && match[1]) { 26 | page.iframe = $sce.trustAsResourceUrl(match[1]) 27 | page.text = page.text.replace(/

    \[(http:\/\/.*)\]<\/p>/, '').trim() 28 | } 29 | page.trustedText = $sce.trustAsHtml(page.text.replace(/

    ( )?<\/p>/,'')) 30 | } 31 | page.trustedVideo = $sce.trustAsResourceUrl(page.video) 32 | page.poster = $sce.trustAsResourceUrl(page.video + '.jpg') 33 | page.contentMinimized = false; 34 | page.toggleMinimizeContent = function(){ 35 | this.contentMinimized = !this.contentMinimized; 36 | setTimeout(Zoomer.windowResized, 100) 37 | } 38 | page.meta = $sce.trustAsHtml( page.meta ); 39 | page.metaB = $sce.trustAsHtml( page.metaB ); 40 | 41 | if( mediaMeta.isActive ) { 42 | // Identify the key - media URL for videos, media ID for zoomers. 43 | var key = null, keyB = null; 44 | switch( page.type ) { 45 | case 'text': 46 | break; 47 | case 'video': 48 | key = page.video; 49 | break; 50 | case 'image': 51 | key = page.image; 52 | break; 53 | case 'comparison': 54 | key = page.image; 55 | keyB = page.imageB; 56 | break; 57 | } 58 | // Look up in mediaMeta hash or fall back to GriotWP value. 59 | if( key ) { 60 | page.meta = mediaMeta.get( key ) || page.meta; 61 | } 62 | if( keyB ) { 63 | page.metaB = mediaMeta.get( keyB ) || page.metaB; 64 | } 65 | } 66 | 67 | // Glance Text 68 | switch( page.type ){ 69 | case 'video': 70 | page.glanceText = 'Tap to play video'; 71 | break; 72 | case 'image': 73 | page.glanceText = 'Press to view image'; 74 | break; 75 | case 'comparison': 76 | page.glanceText = 'Press to view images'; 77 | break; 78 | default: 79 | page.glanceText = 'Press to view'; 80 | } 81 | 82 | }); 83 | 84 | segmentio.track('Opened a Story', {id: $scope.id, name: $scope.story.title}) 85 | }) 86 | 87 | setTimeout(Zoomer.windowResized, 100) 88 | $scope.storyMenuOpen = false 89 | $scope.toggleStoryMenu = function(){ 90 | $scope.storyMenuOpen = !$scope.storyMenuOpen 91 | } 92 | 93 | $scope.activePage = 0 94 | $scope.updateActivePage = function(newPage){ 95 | if((newPage > -1) && (newPage < $scope.story.pages.length)){ 96 | $scope.activePage = newPage 97 | segmentio.track('Paged a Story', {id: $scope.id, name: $scope.story.title, page: newPage}) 98 | } 99 | setTimeout(Zoomer.windowResized, 200) 100 | } 101 | $scope.backToObject=function(){ 102 | $rootScope.nextView = 'more' 103 | if(history.length > 1) { 104 | history.back(); 105 | } else { 106 | window.location = window.location.origin + window.location.pathname 107 | } 108 | } 109 | } 110 | ]) -------------------------------------------------------------------------------- /css/goldweights.css: -------------------------------------------------------------------------------- 1 | /* Goldweights 2 | */ 3 | #goldweights .flatmap .hint { 4 | display: none; 5 | z-index: -1; 6 | } 7 | 8 | #goldweights #zoomer { 9 | height: 551px; 10 | min-height: 551px; 11 | } 12 | 13 | #goldweights ol { 14 | list-style: none; 15 | padding: 0; 16 | margin: 0; 17 | } 18 | 19 | #goldweights ol > li { 20 | display: none; 21 | } 22 | #goldweights ol > li.active { 23 | display: block; 24 | margin: 0 1em 0 5em; 25 | } 26 | #goldweights li.active ~ li.active { 27 | display: none; 28 | } 29 | 30 | #goldweights h3 { 31 | position: absolute; 32 | top:-60px; 33 | left:55px; 34 | } 35 | 36 | #goldweights .play { 37 | /*width: 2.35em; 38 | height: 2.35em;*/ 39 | width:44px; 40 | height:44px; 41 | padding: 1px 0 0 3px; 42 | border: 3px solid #000; 43 | border-radius: 50%; 44 | font-size:1.45em; 45 | background-color:#ededed; 46 | text-align:center; 47 | position: absolute; 48 | top:-55px; 49 | left:2px; 50 | } 51 | #goldweights .play.icon-pause { 52 | padding: 1px 0 0 0px; 53 | } 54 | 55 | #goldweights figure.popup { 56 | position: fixed; 57 | width: 100%; 58 | height: 100vh; 59 | top: 0; 60 | left: 0; 61 | margin: 0; 62 | z-index: 1000; 63 | } 64 | @media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : landscape) { 65 | #goldweights figure.popup { height: 748px; } 66 | } 67 | @media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : portrait) { 68 | #goldweights figure.popup { height: 1004px; } 69 | } 70 | 71 | #goldweights figure.popup .flatmap { 72 | width: 100%; 73 | height: 100%; 74 | z-index: 10000; 75 | } 76 | #goldweights .popup a.icon, .popup aside { 77 | position: absolute; 78 | bottom: 0; 79 | z-index: 10000000; 80 | padding: 10px; 81 | color: black; 82 | background-color: rgba(250,250,250,0.9); 83 | opacity: 1; 84 | max-height: 748px; 85 | overflow-y: scroll; 86 | } 87 | #goldweights .popup a.back { 88 | left: 0; 89 | } 90 | .popup figcaption > a { 91 | font-size: 1em; 92 | font-weight: bold; 93 | text-transform: uppercase; 94 | white-space: nowrap; 95 | } 96 | .popup figcaption > a:before { 97 | font-size: 1.4em; 98 | } 99 | .popup figcaption a.back { 100 | left: 0; 101 | } 102 | .popup figcaption a.credits { 103 | right: 0; 104 | } 105 | #goldweights figcaption aside { 106 | right: 0; 107 | max-width: 45em; 108 | background-color: rgba(250, 250, 250, 0.9); 109 | text-align: right; 110 | } 111 | #goldweights aside > * { 112 | font-weight: 200; 113 | font-size: 0.8rem; 114 | } 115 | 116 | #goldweights aside { 117 | position: relative; 118 | top: 0; 119 | opacity: 1 !important; 120 | } 121 | 122 | #goldweights div.description p:first-child{ 123 | margin-top:65px; 124 | } 125 | 126 | #goldweights div.description { 127 | margin-left:55px; 128 | max-width: 33em; 129 | } 130 | 131 | #goldweights li { 132 | position:relative; 133 | margin-left:55px; 134 | } 135 | 136 | #goldweights li ul{ 137 | list-style-type: none; 138 | margin-left: 55px; 139 | } 140 | 141 | #goldweights li ul li{ 142 | width:25%; 143 | float:left; 144 | margin: 10px 0 !important; 145 | } 146 | #goldweights li ul li.tombstoned { 147 | width: 50%; 148 | position: relative; 149 | } 150 | #goldweights li.tombstoned img { 151 | width: 50%; 152 | } 153 | #goldweights li.tombstoned aside { 154 | position: absolute; 155 | top: 0; 156 | left: 55%; 157 | } 158 | 159 | 160 | body.west { 161 | transition: -webkit-transform 300ms linear; 162 | -webkit-transform-origin: center bottom; 163 | -webkit-transform: rotate(180deg); 164 | margin-bottom: 200px; 165 | max-height: 700px; 166 | } 167 | .west #goldweights #zoomer { 168 | -webkit-transform: rotate(180deg); 169 | } 170 | .west #goldweights #zoomer .noteMarker span { 171 | -webkit-transform: rotate(180deg); 172 | } 173 | #goldweights hr { 174 | clear: both; 175 | display: none; 176 | } 177 | 178 | #goldweights #home { 179 | position: absolute; 180 | top: 567px; 181 | right: 2em; 182 | text-align: right; 183 | } 184 | #goldweights .icon-home { 185 | color: #222; 186 | } 187 | #goldweights #home p { 188 | margin: 0; 189 | } 190 | -------------------------------------------------------------------------------- /fonts/fontello/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "griot", 3 | "css_prefix_text": "icon-", 4 | "css_use_suffix": false, 5 | "hinting": true, 6 | "units_per_em": 1000, 7 | "ascent": 850, 8 | "glyphs": [ 9 | { 10 | "uid": "d3b3f17bc3eb7cd809a07bbd4d178bee", 11 | "css": "resize-vertical", 12 | "code": 59415, 13 | "src": "fontawesome" 14 | }, 15 | { 16 | "uid": "e335adbc2d898c7d85d40c507796e7b4", 17 | "css": "mail", 18 | "code": 59416, 19 | "src": "entypo" 20 | }, 21 | { 22 | "uid": "3ba4275937db277075fc47d6b5a69a2e", 23 | "css": "back", 24 | "code": 59410, 25 | "src": "entypo" 26 | }, 27 | { 28 | "uid": "24aba8757d4a7b49568c98a577a85df3", 29 | "css": "cancel-outline", 30 | "code": 59408, 31 | "src": "typicons" 32 | }, 33 | { 34 | "uid": "1dq4tek4k8ea7zlj4kc3w83itnutaxg5", 35 | "css": "cancel", 36 | "code": 59407, 37 | "src": "typicons" 38 | }, 39 | { 40 | "uid": "517522647a556192fbc78aa391b7b0c2", 41 | "css": "cancel-circled-outline", 42 | "code": 59406, 43 | "src": "typicons" 44 | }, 45 | { 46 | "uid": "3327862df439988139b61814143b7a42", 47 | "css": "cancel-circled", 48 | "code": 59405, 49 | "src": "typicons" 50 | }, 51 | { 52 | "uid": "1gf923f9wvaezxmfon515dglxa3drf0e", 53 | "css": "plus", 54 | "code": 59413, 55 | "src": "typicons" 56 | }, 57 | { 58 | "uid": "qab5uvjyoc3uu4d2pvt8soshtgkuvpak", 59 | "css": "minus", 60 | "code": 59414, 61 | "src": "typicons" 62 | }, 63 | { 64 | "uid": "85fda3129wk4amvn25wrq843sbj3yzl0", 65 | "css": "info", 66 | "code": 59411, 67 | "src": "typicons" 68 | }, 69 | { 70 | "uid": "w3nzesrlbezu6f30q7ytyq919p6gdlb6", 71 | "css": "home", 72 | "code": 59394, 73 | "src": "typicons" 74 | }, 75 | { 76 | "uid": "6a06892e76b81e023da6ddfb38a26b37", 77 | "css": "doc-text", 78 | "code": 59412, 79 | "src": "typicons" 80 | }, 81 | { 82 | "uid": "jh3jpcb1t1bcm80gidkadilh080aq79h", 83 | "css": "menu", 84 | "code": 59395, 85 | "src": "typicons" 86 | }, 87 | { 88 | "uid": "cdfalpadi7huwv9ah4fef2gpfpb4c6qm", 89 | "css": "resize-full", 90 | "code": 59392, 91 | "src": "typicons" 92 | }, 93 | { 94 | "uid": "8d2bc2d959a55e76466bbef6e84c8373", 95 | "css": "resize-normal", 96 | "code": 59393, 97 | "src": "typicons" 98 | }, 99 | { 100 | "uid": "wlri7uftq4zbi82q2xuf08ayd8kpijlk", 101 | "css": "left-open", 102 | "code": 59396, 103 | "src": "typicons" 104 | }, 105 | { 106 | "uid": "6zhrgcf3co77hnljttd3b2mrc8z5fiq5", 107 | "css": "right-open", 108 | "code": 59397, 109 | "src": "typicons" 110 | }, 111 | { 112 | "uid": "qqsxko9kqxh5g53lz33yonkpc2rpkvkn", 113 | "css": "left", 114 | "code": 59398, 115 | "src": "typicons" 116 | }, 117 | { 118 | "uid": "4oaz56fmzfvx7vry2o540n9l4z79fz8t", 119 | "css": "right", 120 | "code": 59399, 121 | "src": "typicons" 122 | }, 123 | { 124 | "uid": "58b78b6ca784d5c3db5beefcd9e18061", 125 | "css": "left-small", 126 | "code": 59400, 127 | "src": "typicons" 128 | }, 129 | { 130 | "uid": "877a233d7fdca8a1d82615b96ed0d7a2", 131 | "css": "right-small", 132 | "code": 59401, 133 | "src": "typicons" 134 | }, 135 | { 136 | "uid": "3e290a111c0f3ee3acbf7b5f17ccc04a", 137 | "css": "play", 138 | "code": 59402, 139 | "src": "typicons" 140 | }, 141 | { 142 | "uid": "e44ef09cb81413287d702eefa65dd790", 143 | "css": "pause", 144 | "code": 59409, 145 | "src": "typicons" 146 | }, 147 | { 148 | "uid": "8f25d96c5665c84e4403538eef14ec2c", 149 | "css": "info-circled", 150 | "code": 59403, 151 | "src": "mfglabs" 152 | }, 153 | { 154 | "uid": "15ba51cbb4d05e6fb88f03a31a7c711c", 155 | "css": "info-circled-alt", 156 | "code": 59404, 157 | "src": "mfglabs" 158 | } 159 | ] 160 | } -------------------------------------------------------------------------------- /js/factories.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieve external data. 3 | */ 4 | 5 | // Tile data 6 | app.factory('tilesaw', ['$http', 'envConfig', function($http, config) { 7 | return { get: function(image) { 8 | return $http.get(config.tilesaw + image + '.tif', {cache: true}).then(function(result) { return result.data; }) 9 | }} 10 | }]) 11 | 12 | // Application content 13 | app.factory('notes', ['$http', 'envConfig', 'miaThumbnailAdapter', '$sce', '$q', function($http, config, thumbs, $sce, $q) { 14 | window.miaThumbnailAdapter = thumbs // TODO: why isn't this injected below? I can't access inside either of the next functions on L15 and 16 15 | window.$q = $q // again, why!? 16 | return function() { 17 | var d = $q.defer() 18 | 19 | $http.get(config.crashpad, {cache: true}).then(function(result) { 20 | result.data.clusters = require('../clusters/clusters.json') // TODO: PS these shouldn't be hardcoded 21 | 22 | angular.forEach(result.data.objects, function(o) { 23 | o.thumbnail = miaThumbnailAdapter.get(o.views[0].image) 24 | o.title = o.title.replace('&', '&') 25 | }) 26 | angular.forEach(result.data.panels, function(panel) { 27 | panel.trustedContent = $sce.trustAsHtml(panel.content) 28 | }) 29 | 30 | return result.data; 31 | }).then(function(data) { 32 | // (artstories-image-dimensions.json is 72kb of JSON generated by a makefile task completely dependent on me) 33 | // TODO: compute this hash of image dimensions automatically, codify it into the image server. 34 | // then maybe serve up only the needed images? 35 | var url = "http://cdn.dx.artsmia.org/thumbs/artstories-image-dimensions.json" 36 | return $http.get(url, {cache: true}).then(function(result) { 37 | var allImageDimensions = result.data 38 | data.objects = angular.forEach(data.objects, function(o, id) { 39 | var dimensions = allImageDimensions[o.views[0].image] 40 | if(!dimensions) return console.info('missing dimensions for', id, o.views[0].image) 41 | o.imageWidth = dimensions[0] 42 | o.imageHeight = dimensions[1] 43 | }) 44 | 45 | d.resolve(data) 46 | }) 47 | }) 48 | 49 | return d.promise 50 | } 51 | }]) 52 | 53 | app.factory('email', ['$http', 'envConfig', function($http, config) { 54 | return { 55 | share: function(email, params) { 56 | return $http.post(config.emailServer + email, params).success(function(response) { 57 | return response 58 | }) 59 | } 60 | } 61 | }]) 62 | 63 | app.factory('initIsotope', ['$rootScope', function($rootScope) { 64 | return function() { 65 | if( window.innerWidth < 768 ) { 66 | $rootScope.loaded = true; 67 | return; 68 | } 69 | 70 | var cover = document.querySelector('#cover'); 71 | 72 | this.iso = new Isotope( cover, { 73 | itemSelector:'.isotope-item', 74 | layoutMode:'masonryHorizontal', 75 | masonryHorizontal: { 76 | rowHeight: 300, 77 | gutter: 2 // this is only for the vertical space between rows, space each element out with margin-right 78 | }, 79 | containerStyle: null, 80 | isInitLayout: false 81 | }); 82 | 83 | var centerCover = function(){ 84 | // Get height of container 85 | var availableHeight = $('.cover-wrapper').height(); 86 | 87 | // Get number of rows - 300px plus 10px gutter. 88 | var rowCount = Math.floor( availableHeight / 302 ) || 1; 89 | 90 | // Get height that will wrap snugly around rows 91 | var newHeight = ( rowCount * 302 ) + 1; 92 | 93 | // Get new top for #cover 94 | var newTop = ( availableHeight - newHeight) / 2; 95 | 96 | // Update cover height and top margin 97 | $('#cover').css({ 98 | 'height': newHeight + 'px', 99 | 'top': newTop + 'px' 100 | }); 101 | 102 | } 103 | 104 | this.iso.on( 'layoutComplete', function(){ 105 | 106 | centerCover(); 107 | 108 | $('.cover-item').css({ 109 | 'opacity':1 110 | }); 111 | 112 | $rootScope.loaded = true; 113 | }); 114 | 115 | $(window).on( 'resize', function(){ 116 | centerCover(); 117 | }); 118 | 119 | this.iso.layout(); 120 | 121 | }; 122 | }]) 123 | -------------------------------------------------------------------------------- /js/directives/note.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates and controls annotation markers on a zoomable image (flatmap). 3 | */ 4 | 5 | app.directive('note', function(segmentio, $sce) { 6 | var divIcon = L.divIcon({className: 'noteMarker'}) 7 | return { 8 | restrict: 'E', 9 | // scope: {note: '=', view: '='}, 10 | controller: function($scope) {}, 11 | require: '^flatmap', 12 | link: function(scope, element, attrs, flatmapCtrl) { 13 | var jsonToLayer = function(note) { 14 | var geometry, json; 15 | if(note.type == 'FeatureCollection') { 16 | json = {type: 'MultiPolygon', coordinates: [].map.call(note.features, function (f) { return [f.geometry.coordinates[0]] })} 17 | } else { 18 | json = note.geometry 19 | } 20 | 21 | return L.GeoJSON.geometryToLayer(json) 22 | } 23 | 24 | var eachMarker = function(callback) { 25 | angular.forEach(scope.markers, callback) 26 | }, 27 | eachLayer = function(layer, callback) { 28 | layer.eachLayer ? layer.eachLayer(callback) : callback(layer) 29 | } 30 | 31 | scope.flatmapCtrl = flatmapCtrl 32 | scope.map = scope.flatmapCtrl.scope.zoom.map 33 | scope.jsonLayer = jsonToLayer(scope.note.geoJSON) 34 | scope.note.index = scope.$parent.$parent.noteCount = (scope.$parent.$parent.noteCount || 0) + 1 35 | divIcon.options.html = "" + scope.note.index + "" 36 | scope.markers = [] 37 | 38 | eachLayer(scope.jsonLayer, function(layer) { 39 | scope.markers.push(L.marker(layer.getBounds().getCenter(), {icon: divIcon})) 40 | }) 41 | 42 | scope.note.active = false 43 | 44 | var zoomNote = function() { 45 | flatmapCtrl.scope.$broadcast('changeView', scope.view) 46 | flatmapCtrl.scope.$broadcast('changeGeometry', scope.jsonLayer) 47 | scope.note.active = true 48 | scope.$$phase || scope.$apply() 49 | scrollNoteTextIntoView() 50 | } 51 | var scrollNoteTextIntoView = function() { // this is hacky 52 | var noteEl = $('#annotations li.note:nth-child(' + (scope.$index+1) + ')')[0] 53 | if(noteEl) noteEl.scrollIntoViewIfNeeded && noteEl.scrollIntoViewIfNeeded() || noteEl.scrollIntoView() 54 | } 55 | var toggleNoteZoom = function() { 56 | scope.$apply(function() { scope.note.active = !scope.note.active }) 57 | } 58 | 59 | scope.$watch('note.active', function(newVal, oldVal) { 60 | var openedOrClosed = undefined 61 | if(!newVal && oldVal && scope.note == flatmapCtrl.scope.lastActiveNote) { 62 | flatmapCtrl.removeJsonLayer() 63 | scope.map.zoomOut(100) 64 | flatmapCtrl.scope.lastActiveNote = null 65 | openedOrClosed = 'Closed' 66 | } else if(newVal && !oldVal) { 67 | var lastNote = flatmapCtrl.scope.lastActiveNote, note = scope.note 68 | if(lastNote) lastNote.active = false 69 | zoomNote() 70 | flatmapCtrl.scope.lastActiveNote = scope.note 71 | openedOrClosed = 'Opened' 72 | scope.$parent.$parent.$parent.glanceText = $sce.trustAsHtml( "Press to view detail " + scope.note.index + "" ); 73 | scope.$$phase || scope.$apply() 74 | } 75 | if(openedOrClosed) segmentio.track(openedOrClosed + ' a Detail', {title: scope.note.title, index: scope.note.index, id: flatmapCtrl.scope.$parent.id}) 76 | 77 | // The active marker goes to the SW (lower-left) corner of bounds 78 | // inactive markers, center of bounds 79 | var layer = scope.jsonLayer, index = 0 80 | eachLayer(layer, function(_layer) { 81 | scope.markers[index].setLatLng(newVal ? _layer._latlngs[0] : _layer.getBounds().getCenter()) 82 | index++ 83 | }) 84 | }) 85 | 86 | flatmapCtrl.scope.$watch('image', function(newVal, oldVal) { 87 | eachMarker(function(marker) { 88 | if(newVal == scope.$parent.view.image) { 89 | marker.setOpacity(1) 90 | marker.on('click', toggleNoteZoom) 91 | } else { 92 | marker.setOpacity(0) 93 | marker.off('click', toggleNoteZoom) 94 | } 95 | }) 96 | }) 97 | 98 | eachMarker(function(marker) { marker.addTo(scope.map) }) 99 | } 100 | } 101 | }) 102 | 103 | -------------------------------------------------------------------------------- /sass/index.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Styles for the cover page. 3 | */ 4 | 5 | .cover-wrapper { 6 | position:absolute; 7 | top:0; 8 | right:0; 9 | bottom:3em; 10 | left:0; 11 | list-style:none; 12 | padding:0; 13 | overflow:auto; 14 | -webkit-overflow-scrolling:touch; 15 | } 16 | 17 | #cover { 18 | position:absolute; 19 | top:0; 20 | right:0; 21 | bottom:0; 22 | left:0; 23 | display:block; 24 | padding: 0 10px; 25 | margin:0; 26 | overflow:auto; 27 | -webkit-overflow-scrolling:touch; 28 | 29 | @media #{$medium}{ 30 | padding:0 0 0 10px; 31 | } 32 | } 33 | 34 | .cover-item { 35 | @include transition( opacity 300ms ease ); 36 | display:block; 37 | vertical-align:top; 38 | margin-right: 2px; 39 | height:auto; 40 | max-height:300px; 41 | max-width:100%; 42 | 43 | @media #{$medium}{ 44 | opacity:0; 45 | } 46 | 47 | &.story { 48 | max-width:100%; 49 | width:300px; 50 | height:300px; 51 | padding:15px; 52 | background-color:#fff; 53 | border:1px solid #999; 54 | } 55 | 56 | &.panel{ 57 | padding:15px; 58 | background-color:#fff; 59 | border:1px solid #999; 60 | max-width:100%; 61 | max-height:100%; 62 | } 63 | 64 | &.panel-big{ 65 | width:610px; 66 | height:610px; 67 | } 68 | 69 | &.panel-portrait{ 70 | width:300px; 71 | height:610px; 72 | } 73 | 74 | &.panel-landscape{ 75 | width:610px; 76 | height:300px; 77 | } 78 | 79 | &.panel-small{ 80 | width:300px; 81 | height:300px; 82 | } 83 | 84 | a { 85 | text-decoration:none; 86 | } 87 | 88 | @media #{$medium} { 89 | height:300px; 90 | margin:0 2px 0 0; 91 | } 92 | } 93 | 94 | .cover-item-image { 95 | display:block; 96 | max-height:100%; 97 | margin:0 auto; 98 | border:1px solid #999; 99 | } 100 | 101 | .cover-item-title { 102 | font-size:1.1em; 103 | color:#222; 104 | text-align:center; 105 | line-height:1.1; 106 | padding:0; 107 | margin:0; 108 | 109 | .object & { 110 | display:none; 111 | } 112 | } 113 | 114 | .cover-item-poster{ 115 | width:100%; 116 | height:200px; 117 | background-size:cover; 118 | background-position:50% 50%; 119 | border:1px solid #999; 120 | } 121 | 122 | .cover-item-caption { 123 | position:relative; 124 | height:85px; 125 | margin:0; 126 | padding:10px; 127 | } 128 | 129 | .cover-panel-wrap{ 130 | height:100%; 131 | } 132 | 133 | .cover-instructions { 134 | @include box-shadow(0 -3px 5px rgba(0,0,0,0.23)); 135 | position:absolute; 136 | display:block; 137 | bottom:0; 138 | font-size:1em; 139 | font-style:italic; 140 | text-align:center; 141 | width:100%; 142 | height:50px; 143 | line-height:50px; 144 | vertical-align:middle; 145 | background-color:#ddd; 146 | } 147 | 148 | .cover-instructions * { 149 | display: inline; 150 | font-size: 100%; 151 | } 152 | 153 | .cover-instructions p { 154 | display: none; 155 | } 156 | 157 | .clusters img { 158 | max-width: 30%; 159 | } 160 | 161 | .clusters li { 162 | display: inline; 163 | list-style: none; 164 | -webkit-columns: 3; 165 | } 166 | 167 | aside.splash.show { 168 | z-index: 10; 169 | opacity: 1; 170 | } 171 | 172 | aside.splash { 173 | z-index: -1; 174 | opacity: 0; 175 | display: block; 176 | -webkit-transition: all 0.5s; 177 | text-align: center; 178 | font-family: MiaGrotesk-Light; 179 | font-size: 234%; 180 | height: 100%; 181 | position: relative; 182 | } 183 | 184 | aside.splash h1 { 185 | text-transform: lowercase; 186 | font-family: MiaGrotesk-Bold, sans-serif; 187 | font-size: 3em; 188 | margin: 0 0 -.25em 0; 189 | color: #333; 190 | } 191 | 192 | aside.splash #placard { 193 | background-color: rgba(255, 255, 255, 0.7); 194 | vertical-align: middle; 195 | padding: 1em; 196 | position: relative; 197 | top: 50%; 198 | transform: translateY(-50%); 199 | -webkit-transform: translateY(-50%); 200 | max-width: 100%; 201 | } 202 | @media #{$medium}{ 203 | aside.splash h1 { 204 | font-size: 4em; 205 | } 206 | aside.splash #placard > p { 207 | padding: 0 2em; 208 | } 209 | } 210 | 211 | #ethnio-screener-48826 { 212 | top: auto !important; 213 | bottom: 0; 214 | } 215 | #ethnio-screener-48826 > div:first-child { 216 | padding: 0 !important; 217 | margin: 9px 0; 218 | } 219 | #ethnio-screener-id-48826 { 220 | top: auto !important; 221 | bottom: 0px; 222 | } 223 | -------------------------------------------------------------------------------- /js/adapters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adapters (MIA-specific) 3 | * 4 | * These adapters are specific to the MIA's implementation of Griot. You should 5 | * overwrite them if you'd like to use your own service to pull data. If you'd 6 | * rather pull all data from GriotWP or another service, set 7 | * config.miaMediaMetaActive, config.miaObjectMetaActive, and 8 | * config.miaThumbnailAdapterActive to false in config.js. 9 | */ 10 | 11 | /** 12 | * miaMediaMetaAdapter 13 | * 14 | * Grabs media metadata from an external source and provides a method for 15 | * retrieving that metadata by ID. 16 | */ 17 | app.service( 'miaMediaMetaAdapter', function( $http, $sce, envConfig ) { 18 | 19 | var _this = this; 20 | 21 | this.isActive = false; 22 | 23 | this.metaHash = {}; 24 | 25 | this.get = function( id ) { 26 | return _this.metaHash[ id ] || false; 27 | } 28 | 29 | this.build = function( src ){ 30 | 31 | $http.get( src, { cache: true } ).success( function( result ) { 32 | 33 | _this.isActive = true; 34 | 35 | for( var id in result ) { 36 | var description = result[ id ].description ? result[id].description + "
    " : ''; 37 | var credit = result[ id ].credit || ''; 38 | _this.metaHash[ id ] = $sce.trustAsHtml((description + credit).replace(/\n/g, '
    \n')); 39 | } 40 | 41 | }); 42 | 43 | } 44 | 45 | }); 46 | 47 | /** 48 | * miaObjectMetaAdapter 49 | * 50 | * Grabs object metadata from an external source and provides a method for 51 | * retrieving metadata reformatted into a particular grouping, i.e. to match 52 | * the groups in GriotWP. 53 | */ 54 | app.service( 'miaObjectMetaAdapter', function( $http, $sce, envConfig ) { 55 | var _this = this; 56 | 57 | this.isActive = envConfig.miaObjectMetaActive; 58 | 59 | this.metaHash = {}; 60 | 61 | this.get = function( id, grouping ) { 62 | try{ 63 | if (_this.metaHash[id] !== undefined) { 64 | var hash = _this.metaHash[id] || _this.metaHash[parseInt(id)] 65 | return grouping ? hash[grouping] : hash 66 | } else { 67 | return this.getFromAPI(id, grouping) 68 | } 69 | } catch(e) { 70 | console.log('error in objectMeta.get', e) 71 | return null; 72 | } 73 | } 74 | 75 | this.getFromAPI = function(id, grouping) { 76 | var apiURL = envConfig.miaObjectMetaSrc+id 77 | return $http.get(apiURL, {cache: true}).then(function(result) { 78 | var data = result.data 79 | if(id.match('/')) id = data.id.split('/').reverse()[0] 80 | _this.addObjectToMetaHash(id, data) 81 | return _this.get(id, grouping) 82 | }) 83 | } 84 | 85 | this.addObjectToMetaHash = function(id, json) { 86 | var groupings = {}, 87 | artist, 88 | culture, 89 | country, 90 | dated, 91 | medium, 92 | dimension, 93 | creditline, 94 | accession_number, 95 | trustedDescription; 96 | 97 | artist = json.artist || 'Artist unknown'; 98 | culture = json.culture || ''; 99 | country = json.country || ''; 100 | dated = json.dated || ''; 101 | medium = json.medium || ''; 102 | dimension = json.dimension || ''; 103 | creditline = json.creditline || ''; 104 | accession_number = json.accession_number || ''; 105 | trustedDescription = $sce.trustAsHtml( json.description ); 106 | 107 | groupings.meta1 = artist + ', ' + ( culture && culture + ', ' ) + country; 108 | groupings.meta2 = dated; 109 | groupings.meta3 = $sce.trustAsHtml( ( medium && medium + "
    " ) + ( dimension && dimension + "
    " ) + ( creditline && creditline + "
    " ) + accession_number ); 110 | 111 | // Special editions for goldweights 112 | groupings.gw_title = $sce.trustAsHtml( json.title ); 113 | groupings.gw_meta2 = $sce.trustAsHtml( ( creditline && creditline + "
    " ) + accession_number ); 114 | groupings.location = json.room.replace('G', '') 115 | 116 | this.metaHash[id] = groupings; 117 | return groupings 118 | } 119 | 120 | }); 121 | 122 | /** 123 | * miaThumbnailAdapter 124 | * 125 | * Provides a method for retrieving an image thumbnail from an external source, 126 | * given an image ID. 127 | */ 128 | app.service( 'miaThumbnailAdapter', function() { 129 | 130 | var _this = this; 131 | 132 | this.isActive = false; 133 | 134 | this.cdn = ''; 135 | 136 | this.init = function( cdn ) { 137 | _this.isActive = true; 138 | _this.cdn = cdn; 139 | } 140 | 141 | this.get = function( id ) { 142 | if(id === undefined) return 143 | var trimmed_id = id.replace( '.tif', '' ); 144 | return _this.cdn + 'thumbs/tn_' + trimmed_id + '.jpg'; 145 | } 146 | 147 | }); 148 | -------------------------------------------------------------------------------- /js/vendor/masonry-horizontal.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * masonryHorizontal layout mode for Isotope 3 | * http://isotope.metafizzy.co 4 | */ 5 | 6 | /*jshint browser: true, strict: true, undef: true, unused: true */ 7 | 8 | ( function( window ) { 9 | 10 | 'use strict'; 11 | 12 | // -------------------------- helpers -------------------------- // 13 | 14 | var indexOf = Array.prototype.indexOf ? 15 | function( items, value ) { 16 | return items.indexOf( value ); 17 | } : 18 | function ( items, value ) { 19 | for ( var i=0, len = items.length; i < len; i++ ) { 20 | var item = items[i]; 21 | if ( item === value ) { 22 | return i; 23 | } 24 | } 25 | return -1; 26 | }; 27 | 28 | // -------------------------- definition -------------------------- // 29 | 30 | function masonryHorizontalDefinition( getSize, LayoutMode ) { 31 | // create an Outlayer layout class 32 | var MasonryHorizontal = LayoutMode.create('masonryHorizontal'); 33 | 34 | MasonryHorizontal.prototype._resetLayout = function() { 35 | this.getRowHeight(); 36 | this._getMeasurement( 'gutter', 'outerHeight' ); 37 | 38 | this.rowHeight += this.gutter; 39 | // measure rows 40 | this.rows = Math.floor( ( this.isotope.size.innerHeight + this.gutter ) / this.rowHeight ); 41 | this.rows = Math.max( this.rows, 1 ); 42 | 43 | // reset row Xs 44 | var i = this.rows; 45 | this.rowXs = []; 46 | while (i--) { 47 | this.rowXs.push( 0 ); 48 | } 49 | 50 | this.maxX = 0; 51 | }; 52 | 53 | MasonryHorizontal.prototype._getItemLayoutPosition = function( item ) { 54 | item.getSize(); 55 | // how many rows does this brick span 56 | var rowSpan = Math.ceil( item.size.outerHeight / this.rowHeight ); 57 | rowSpan = Math.min( rowSpan, this.rows ); 58 | 59 | var rowGroup = this._getRowGroup( rowSpan ); 60 | // get the minimum X value from the rows 61 | var minimumX = Math.min.apply( Math, rowGroup ); 62 | var shortRowIndex = indexOf( rowGroup, minimumX ); 63 | 64 | // position the brick 65 | var position = { 66 | x: minimumX, 67 | y: this.rowHeight * shortRowIndex 68 | }; 69 | 70 | // apply setHeight to necessary rows 71 | var setWidth = minimumX + item.size.outerWidth; 72 | var setSpan = this.rows + 1 - rowGroup.length; 73 | for ( var i = 0; i < setSpan; i++ ) { 74 | this.rowXs[ shortRowIndex + i ] = setWidth; 75 | } 76 | 77 | return position; 78 | }; 79 | 80 | /** 81 | * @param {Number} rowSpan - number of rows the element spans 82 | * @returns {Array} rowGroup 83 | */ 84 | MasonryHorizontal.prototype._getRowGroup = function( rowSpan ) { 85 | if ( rowSpan < 2 ) { 86 | // if brick spans only one row, use all the row Xs 87 | return this.rowXs; 88 | } 89 | 90 | var rowGroup = []; 91 | // how many different places could this brick fit horizontally 92 | var groupCount = this.rows + 1 - rowSpan; 93 | // for each group potential horizontal position 94 | for ( var i = 0; i < groupCount; i++ ) { 95 | // make an array of rowX values for that one group 96 | var groupRowXs = this.rowXs.slice( i, i + rowSpan ); 97 | // and get the max value of the array 98 | rowGroup[i] = Math.max.apply( Math, groupRowXs ); 99 | } 100 | return rowGroup; 101 | }; 102 | 103 | MasonryHorizontal.prototype._manageStamp = function( stamp ) { 104 | var stampSize = getSize( stamp ); 105 | var offset = this.isotope._getElementOffset( stamp ); 106 | // get the rows that this stamp affects 107 | var firstY = this.isotope.options.isOriginTop ? offset.top : offset.bottom; 108 | var lastY = firstY + stampSize.outerHeight; 109 | var firstRow = Math.floor( firstY / this.rowHeight ); 110 | firstRow = Math.max( 0, firstRow ); 111 | var lastRow = Math.floor( lastY / this.rowHeight ); 112 | lastRow = Math.min( this.rows - 1, lastRow ); 113 | // set rowXs to outside edge of the stamp 114 | var stampMaxX = ( this.isotope.options.isOriginLeft ? offset.left : offset.right ) + 115 | stampSize.outerWidth; 116 | for ( var i = firstRow; i <= lastRow; i++ ) { 117 | this.rowXs[i] = Math.max( stampMaxX, this.rowXs[i] ); 118 | } 119 | }; 120 | 121 | MasonryHorizontal.prototype._getContainerSize = function() { 122 | this.maxX = Math.max.apply( Math, this.rowXs ); 123 | 124 | return { 125 | width: this.maxX 126 | }; 127 | }; 128 | 129 | MasonryHorizontal.prototype.needsResizeLayout = function() { 130 | return this.needsVerticalResizeLayout(); 131 | }; 132 | 133 | return MasonryHorizontal; 134 | 135 | } 136 | 137 | if ( typeof define === 'function' && define.amd ) { 138 | // AMD 139 | define( [ 140 | 'get-size/get-size', 141 | 'isotope/js/layout-mode' 142 | ], 143 | masonryHorizontalDefinition ); 144 | } else { 145 | // browser global 146 | masonryHorizontalDefinition( 147 | window.getSize, 148 | window.Isotope.LayoutMode 149 | ); 150 | } 151 | 152 | })( window ); 153 | -------------------------------------------------------------------------------- /js/directives/flatmap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a zoomable image element. 3 | */ 4 | 5 | app.directive('flatmap', function(tilesaw, envConfig, $rootScope ) { 6 | return { 7 | restrict: 'E', 8 | scope: { 9 | json: '@', 10 | image: '@' 11 | }, 12 | replace: true, 13 | transclude: true, 14 | template: '

    ', 15 | controller: function($scope, $element, $attrs ) { 16 | var scope = $scope 17 | scope.$parent.flatmapScope = scope 18 | scope.zoomed = $rootScope.zoomed 19 | 20 | var removeJsonLayer = function() { 21 | if(scope.jsonLayer) scope.zoom.map.removeLayer(scope.jsonLayer) 22 | if(scope.inverseLayer) scope.zoom.map.removeLayer(scope.inverseLayer) 23 | } 24 | 25 | var showJsonLayer = function(fadeAfter, inverse) { 26 | if(!scope.jsonLayer) return 27 | var layerStyle = {stroke: true, fill: false, weight: 2, color: '#eee', opacity: '0.5'}, 28 | addLayer = null 29 | 30 | if(inverse) { 31 | var holes = [] 32 | scope.jsonLayer._latlngs ? holes.push(scope.jsonLayer._latlngs) 33 | : scope.jsonLayer.eachLayer(function(l) { holes.push(l._latlngs) }) 34 | scope.inverseLayer = L.polygon([scope.zoom.imageBounds.toPolygon()._latlngs].concat(holes)) 35 | scope.inverseLayer.setStyle({fill: true, fillColor: '#000', fillOpacity: '0.5', stroke: false}) 36 | addLayer = scope.inverseLayer 37 | } else { 38 | scope.jsonLayer.setStyle(layerStyle) 39 | addLayer = scope.jsonLayer 40 | } 41 | scope.zoom.map.addLayer(addLayer) 42 | // if(fadeAfter) setTimeout(removeJsonLayer, fadeAfter) 43 | } 44 | 45 | var loadImage = function(image) { 46 | if(image === 'undefined' || image === '') return 47 | scope.viewChanging = true 48 | scope.image = image 49 | removeJsonLayer(); scope.jsonLayer = null 50 | tilesaw.get(image).then(function(tileJson) { 51 | $('#'+scope.container).find('.leaflet-tile-pane').css('visibility', 'visible') // why is this necessary? when I re-init a zoomer it's visibility is hidden. 52 | var tileUrl = envConfig.tileUrlSubdomain(tileJson.tiles[0]) 53 | scope.$parent.$parent.tileJson = tileJson 54 | scope.$parent.$parent.imageAspectRatio = tileJson.width / tileJson.height 55 | scope.zoom = Zoomer.zoom_image({container: scope.container, tileURL: tileUrl, imageWidth: tileJson.width, imageHeight: tileJson.height}) 56 | scope.$emit('viewChanged') 57 | scope.$parent.mapLoaded = true 58 | var watchForZoom = scope.zoom.map.on('zoomstart', function() { 59 | scope.$emit('zoom'); 60 | (scope.$$phase || $rootScope.$$phase) || scope.$apply(function() { $rootScope.zoomed = scope.zoomed = true }) 61 | scope.zoom.map.off(watchForZoom) 62 | }) 63 | }) 64 | } 65 | loadImage(scope.image) 66 | 67 | var annotateAndZoom = function(geometry) { 68 | removeJsonLayer() 69 | if(geometry) { 70 | if(geometry._initHooksCalled) { // it's a leaflet object, probably layer 71 | scope.jsonLayer = geometry 72 | } else { 73 | scope.jsonLayer = L.GeoJSON.geometryToLayer(geometry) 74 | } 75 | } 76 | if(scope.viewChanging) return // hold off until the view changes, resulting in `viewChanged` triggering this again 77 | if(scope.jsonLayer) { 78 | scope.$parent.$broadcast('showAnnotationsPanel', 'annotations') 79 | var map = scope.zoom.map, 80 | mapBounds = map.getBounds(), 81 | jsonLayerBounds = scope.jsonLayer.getBounds(), 82 | delay = 0 83 | if(mapBounds.intersects(jsonLayerBounds) || mapBounds.contains(jsonLayerBounds)) { 84 | } else { 85 | // Zoomer is misbehaving when zooming outside the current bounds, plus the zoom all the way out and back in thing is cool 86 | setTimeout(function() { map.zoomOut(100) }, 300) 87 | delay = 1000 88 | } 89 | setTimeout(function() { showJsonLayer(3000, true) }, delay) 90 | setTimeout(function() { map.fitBounds(scope.jsonLayer.getBounds()) }, delay+250) 91 | } 92 | } 93 | 94 | scope.$on('changeGeometry', function(event, geometry) { annotateAndZoom(geometry) }, true) 95 | scope.$on('viewChanged', function(event, message) { scope.viewChanging = false; annotateAndZoom() }, true) 96 | scope.$on('changeView', function(event, message) { 97 | if(message.image != scope.image) loadImage(message.image) 98 | }) 99 | 100 | // TODO: get this working better 101 | scope.$on('viewChanged', function() { 102 | scope.zoom.map.on('zoomedBeyondMin', function(e) { 103 | if(scope.$parent && scope.$parent.notes && scope.$parent.notes.length > 1 && scope.$parent.changeZoomerForViews) 104 | scope.$parent.changeZoomerForViews(this, scope) 105 | }) 106 | }) 107 | 108 | return { 109 | loadImage: loadImage, 110 | annotateAndZoom: annotateAndZoom, 111 | removeJsonLayer: removeJsonLayer, 112 | showJsonLayer: showJsonLayer, 113 | scope: scope 114 | } 115 | }, 116 | link: function(scope, element, attrs) { 117 | scope.container = 'zoom-' + scope.image.replace('+', '-') + '-' + new Date().getUTCMilliseconds() 118 | element.attr('id', scope.container) 119 | } 120 | } 121 | }) 122 | 123 | -------------------------------------------------------------------------------- /js/controllers/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller for cover page (index template). 3 | */ 4 | 5 | app.controller('mainCtrl', ['$scope', '$routeParams', 'segmentio', '$rootScope', '$timeout', 'orderByFilter', 'miaThumbnailAdapter', '$sce', 'resolvedNotes', 'initIsotope', '$location', 6 | function($scope, $routeParams, segmentio, $rootScope, $timeout, orderByFilter, thumbnailAdapter, $sce, notes, initIsotope, $location) { 7 | var dc = $rootScope.defaultCluster 8 | if(dc && dc !== 'highlights') $location.path('/clusters/'+$rootScope.defaultCluster) 9 | 10 | var data = $scope.data = notes 11 | $rootScope.loaded = false 12 | 13 | var cluster = $rootScope.defaultCluster || $routeParams.cluster || 'highlights' 14 | var clusterObjectIds = data.clusters[cluster.replace(/^(g)?(\d+)/i, '$1$2')] 15 | if(clusterObjectIds) { 16 | $scope.cluster = $rootScope.defaultCluster = cluster 17 | $scope.isGallery = cluster.match(/^G\d/i) 18 | $scope.gallery = $scope.cluster.replace('G', '') 19 | $rootScope.showingCluster = $scope.showingCluster = 20 | (typeof $rootScope.showingCluster === 'undefined') ? true : $rootScope.showingCluster 21 | if(!$rootScope.clusterObjects) { 22 | $scope.clusterObjects = clusterObjectIds.map(function(objectId) { 23 | var isStory = objectId.match && objectId.match(/stories\/(\d+)/) 24 | if(isStory) return data.stories[isStory[1]] 25 | return data.objects[objectId] 26 | }).filter(function(o) { return o }) 27 | var startPanels = addPanelsToClusterObjects() 28 | $rootScope.clusterObjects = $scope.clusterObjects = orderByFilter($scope.clusterObjects, random) 29 | startPanels.map(function(p) { $scope.clusterObjects.unshift(p) }) 30 | 31 | $rootScope.otherObjects = $scope.otherObjects = $rootScope.otherObjects = loadAllObjects() 32 | } else { 33 | $scope.clusterObjects = $rootScope.clusterObjects 34 | $scope.otherObjects = $rootScope.otherObjects 35 | } 36 | } else { // not a valid cluster 37 | $location.path('/') 38 | } 39 | 40 | $scope.toggleSeeAll = function() { 41 | var container = $('.cover-wrapper')[0] 42 | if($scope.showingCluster) $timeout(function() { container.scrollLeft = container.scrollWidth-window.innerWidth }, 0) 43 | $scope.loading = true 44 | $rootScope.showingCluster = $scope.showingCluster = !$scope.showingCluster 45 | 46 | $timeout(function() { 47 | $timeout(initIsotope, 0) 48 | $scope.loading = false 49 | }, 0) 50 | } 51 | 52 | // Maintain the random order of the cluster objects, and add all the others 53 | // in random order. Then any 'end' panels. 54 | function loadAllObjects() { 55 | var c = $scope.clusterObjects 56 | var others = [] 57 | 58 | angular.forEach(data.objects, function(o) { 59 | (c.indexOf(o) > -1) || others.push(o) 60 | }) 61 | others = orderByFilter(others, random) 62 | 63 | angular.forEach(data.panels, function(p) { 64 | if(c.indexOf(p) == -1 && p.position == 'end') others.push(p) 65 | }) 66 | 67 | return others 68 | return angular.copy(c).map(function(o) { 69 | // angular.copy and $sce.trust don't work together. this prevents an sce.unsafe 70 | if(o.recordType == 'panel') o.trustedContent = $sce.trustAsHtml(o.content) 71 | return o 72 | }).concat(others) 73 | } 74 | 75 | // Add the panels that should be randomized to `clusterObjects` and 76 | // return the panels that need to be at the beginning so they can be 77 | // `unshifted` after randomization happens 78 | // TODO: should panels go into the cluster objects or the other objects? 79 | function addPanelsToClusterObjects() { 80 | var panels = [] 81 | angular.forEach(data.panels, function(p) { panels.push(p) }) 82 | panels.filter(function(p) { return p.position == 'random' }) 83 | .map(function(p) { $scope.clusterObjects.push(p) }) 84 | return panels.filter(function(p) { return p.position == 'start' }) 85 | } 86 | 87 | $rootScope.nextView = undefined 88 | 89 | $timeout(initIsotope, 0) 90 | 91 | function random() { return 0.5 - Math.random() } 92 | 93 | if(!$rootScope.identifier) { 94 | var adjs = ["autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", 95 | "dark", "summer", "icy", "delicate", "quiet", "white", "cool", "spring", 96 | "patient", "twilight", "dawn", "crimson", "wispy", "weathered", "blue"] 97 | , nouns = ["waterfall", "river", "breeze", "moon", "rain", "wind", "sea", 98 | "morning", "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", 99 | "glitter", "forest", "hill", "cloud", "meadow", "sun", "glade", "bird", 100 | "brook", "butterfly", "bush", "dew", "dust", "field", "fire", "flower"] 101 | , number = Math.floor(Math.random()*100) 102 | , name = adjs[Math.floor(Math.random()*(adjs.length-1))]+"-"+nouns[Math.floor(Math.random()*(nouns.length-1))]+"-"+number 103 | $rootScope.identifier = name 104 | segmentio.identify(name) 105 | } 106 | 107 | segmentio.track('Landed on the homepage') 108 | 109 | // When returning to the home page from an object page, scroll to 110 | // that object. 111 | $rootScope.$watch('loaded', function(val) { 112 | if(val && $rootScope.lastObjectId) { 113 | var lastObjContainer = $('a[href*='+$rootScope.lastObjectId+']').parent() 114 | lastObjContainer && lastObjContainer[0].scrollIntoView() 115 | } 116 | }) 117 | 118 | if(typeof $rootScope.showSplash == 'undefined') $rootScope.showSplash = true 119 | $scope.closeSplashScreen = function() { 120 | $rootScope.showSplash = false 121 | } 122 | } 123 | ]) 124 | -------------------------------------------------------------------------------- /views/object.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 5 | 6 |
    7 | 8 | 9 |
    10 |
    11 |
    12 | 13 | 14 |
    19 | 20 |
    21 |

    {{glanceText}}

    22 |
    23 | 24 |
    25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |

    33 | 34 |
    35 |
    {{wp.meta1}}
    36 |
    {{wp.meta2}}
    37 |
    38 | 39 |
    40 | 41 | 46 | 47 |
    48 | 49 | 50 |
    51 |

    52 | 56 |
    57 | 58 |
      59 |
    1. 60 | 61 |

      {{note.index}}{{note.title || 'Annotation'}}

      62 |
      63 | 64 |
      65 | 66 |

      Tap to expand

      67 |
      68 | 69 |
      70 |
    2. 71 |
    72 | 73 |
    74 |
    Interactive Features
    75 | 80 |
    81 |
    More Views
    82 | 83 |
    84 |
    85 | 86 |
    87 | 88 |
    89 |
    90 | 91 | 92 |
    93 | 94 | 98 | 99 | 100 |
    101 | 102 | 103 | 104 |
    105 | -------------------------------------------------------------------------------- /sass/story.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Story template styles. 3 | */ 4 | 5 | /** 6 | * Header 7 | */ 8 | .story-title{ 9 | @include truncate; 10 | @include box-shadow( 0 5px 5px rgba(0,0,0,0.13) ); 11 | position:absolute; 12 | display:block; 13 | top:0; 14 | left:0; 15 | height:40px; 16 | width:100%; 17 | margin:0; 18 | background-color:#222; 19 | text-align:center; 20 | font-size:1.1em; 21 | color:#fff; 22 | font-weight:600; 23 | line-height:40px; 24 | padding:0 45px; 25 | overflow:hidden; 26 | z-index:2; 27 | 28 | @media #{$medium}{ 29 | font-size:1.3em; 30 | } 31 | } 32 | 33 | /** 34 | * Slider structure 35 | */ 36 | #story { 37 | position:absolute; 38 | top:0; 39 | right:0; 40 | bottom:0; 41 | left:0; 42 | padding:40px 0; 43 | overflow:hidden; 44 | background: #ffffff; 45 | background: -moz-radial-gradient(center, ellipse cover, #ffffff 0%, #e5e5e5 100%); 46 | background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#ffffff), color-stop(100%,#e5e5e5)); 47 | background: -webkit-radial-gradient(center, ellipse cover, #ffffff 0%,#e5e5e5 100%); 48 | background: -o-radial-gradient(center, ellipse cover, #ffffff 0%,#e5e5e5 100%); 49 | background: -ms-radial-gradient(center, ellipse cover, #ffffff 0%,#e5e5e5 100%); 50 | background: radial-gradient(ellipse at center, #ffffff 0%,#e5e5e5 100%); 51 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e5e5e5',GradientType=1 ); 52 | } 53 | .pages-wrap{ 54 | @include transition( all 500ms cubic-bezier(0.81, 0.11, 0.11, 0.91) ); 55 | position:relative; 56 | top:0; 57 | left:0; 58 | width:100%; 59 | height:100%; 60 | white-space:nowrap; 61 | } 62 | .page{ 63 | position:relative; 64 | display:inline-block; 65 | width:100%; 66 | height:100%; 67 | vertical-align:top; 68 | white-space:normal; 69 | overflow:hidden; 70 | background-color:#ededed; 71 | } 72 | 73 | 74 | /** 75 | * Illustration 76 | */ 77 | .story-illustration { 78 | @include transition( right 100ms ease, bottom 100ms ease ); 79 | @include box-shadow( 0px 3px 5px rgba(0,0,0,0.23) ); 80 | position:absolute; 81 | top:0; 82 | right:0; 83 | bottom:0; 84 | left:0; 85 | z-index:1; 86 | background-color:#ddd; 87 | 88 | .contentMinimized & { 89 | bottom:45px; 90 | } 91 | 92 | @media #{$medium} { 93 | top:0; 94 | right:33%; 95 | bottom:0; 96 | left:0; 97 | 98 | .contentMinimized & { 99 | right:60px; 100 | bottom:0; 101 | } 102 | 103 | } 104 | } 105 | .story-video, 106 | .story-image, 107 | .story-comparison, 108 | .story-iframe{ 109 | position:relative; 110 | width:100%; 111 | height:100%; 112 | } 113 | .story-video video{ 114 | @include transition( top 100ms linear, max-height 100ms ease ); 115 | display:block; 116 | margin:0px auto; 117 | } 118 | .story-image .flatmap{ 119 | position:absolute; 120 | top:0; 121 | right:0; 122 | bottom:0; 123 | left:0; 124 | } 125 | .story-comparison .flatmap-a{ 126 | position:absolute; 127 | top:0; 128 | right:50%; 129 | bottom:0; 130 | left:0; 131 | } 132 | .story-comparison .flatmap-b{ 133 | position:absolute; 134 | top:0; 135 | right:0; 136 | bottom:0; 137 | left:50%; 138 | border-left:1px solid #ccc; 139 | } 140 | 141 | 142 | /** 143 | * Content 144 | */ 145 | .story-content-container{ 146 | position:absolute; 147 | top:0; 148 | right:0; 149 | width:33%; 150 | height:100%; 151 | padding:25px; 152 | background-color:#ededed; 153 | } 154 | .story-content{ 155 | position:absolute; 156 | top:0; 157 | right:0; 158 | bottom:0; 159 | left:0; 160 | overflow-x:visible; 161 | overflow-y:auto; 162 | } 163 | .story-home-link { 164 | @extend .home-icon; 165 | position:relative; 166 | display:block; 167 | margin:15px 25px 15px; 168 | } 169 | .story-content .caption { 170 | margin:0 25px 15px; 171 | } 172 | 173 | .story-content .caption p{ 174 | margin:0 0 1em 0; 175 | 176 | @media #{$medium} { 177 | margin:0 60px 1em 0; 178 | &:last-child{ 179 | margin-bottom:0; 180 | } 181 | } 182 | } 183 | 184 | .story-content aside { 185 | @include meta; 186 | margin:0 25px 25px; 187 | 188 | @media #{$medium} { 189 | margin:0 60px 25px 25px; 190 | } 191 | } 192 | 193 | 194 | /** 195 | * Action menu 196 | */ 197 | .story-menu { 198 | @include box-shadow( 0px -3px 5px rgba(0,0,0,0.23) ); 199 | position:absolute; 200 | right:0; 201 | bottom:0; 202 | left:0; 203 | height:45px; 204 | margin:0; 205 | padding:0; 206 | text-align:center; 207 | background-color:#ddd; 208 | z-index:2; 209 | 210 | @media #{$medium} { 211 | @include box-shadow(none); 212 | position:absolute; 213 | top:0; 214 | right:0; 215 | bottom:auto; 216 | left:auto; 217 | width:35px; 218 | height:35px; 219 | padding:0; 220 | background-color:transparent; 221 | } 222 | } 223 | 224 | .story-menu .toggle-content { 225 | @extend .minimize-icon; 226 | @include box-shadow( none ); 227 | position:relative; 228 | display:inline-block; 229 | margin:0; 230 | border:1px solid #ccc; 231 | z-index:1; 232 | 233 | @media #{$medium} { 234 | position:absolute; 235 | display:block; 236 | top:10px; 237 | right:8px; 238 | } 239 | 240 | } 241 | 242 | .story-nav { 243 | @include box-shadow( 0 -5px 5px rgba(0,0,0,0.13) ); 244 | position:fixed; 245 | left:0; 246 | bottom:0; 247 | width:100%; 248 | height:40px; 249 | z-index:3; 250 | background-color:#222; 251 | } 252 | .story-nav-pager{ 253 | text-align:center; 254 | font-size:0.8em; 255 | text-transform:uppercase; 256 | color:#fff; 257 | line-height:40px; 258 | vertical-align:middle; 259 | margin:0; 260 | } 261 | 262 | .story-return{ 263 | @extend .nav-button; 264 | @extend .return-icon; 265 | } 266 | .story-prev-page{ 267 | @extend .nav-button; 268 | @extend .nav-button-left; 269 | @extend .prev-icon; 270 | } 271 | .story-next-page{ 272 | @extend .nav-button; 273 | @extend .nav-button-right; 274 | @extend .next-icon; 275 | } -------------------------------------------------------------------------------- /clusters/clusters.json: -------------------------------------------------------------------------------- 1 | { 2 | "highlights": [ 3 | 278, 4 | 529, 5 | 1218, 6 | 1226, 7 | 1244, 8 | 1348, 9 | 1355, 10 | 1380, 11 | 4866, 12 | 8023, 13 | 1629, 14 | 1721, 15 | 3183, 16 | 3520, 17 | 60728, 18 | 113926, 19 | 114602, 20 | 108860, 21 | 109118, 22 | 115836, 23 | 116725, 24 | 1270, 25 | 90742, 26 | 1411, 27 | 1748, 28 | 4324, 29 | 5788 30 | ], 31 | "G200": [ 32 | 5788, 33 | 116725, 34 | 108860, 35 | 60728, 36 | 4324, 37 | 1380, 38 | 4379, 39 | 117153, 40 | 118304, 41 | 7505, 42 | 22412, 43 | 114429, 44 | 376, 45 | 4829 46 | ], 47 | "G230": [ 48 | 3520, 49 | 3183, 50 | 12111, 51 | 3778, 52 | 4866, 53 | 1312, 54 | 111893, 55 | 111879, 56 | 97, 57 | 111088, 58 | 115514, 59 | 1358, 60 | 1854, 61 | 60728, 62 | 4324, 63 | 1380, 64 | 4379, 65 | 117153, 66 | 118304, 67 | 7505, 68 | 22412, 69 | 114429, 70 | 376, 71 | 4829 72 | ], 73 | "G258": [ 74 | 3520, 75 | 3183, 76 | 12111, 77 | 3778, 78 | 4866, 79 | 1312, 80 | 111893, 81 | 111879, 82 | 97, 83 | 111088, 84 | 115514, 85 | 1358, 86 | 1854, 87 | 114833, 88 | 115320, 89 | 1937, 90 | 111099, 91 | 108767, 92 | 45269, 93 | 114602, 94 | 678, 95 | 112568 96 | ], 97 | "G260": [ 98 | 3183, 99 | 12111, 100 | 3778, 101 | 4866, 102 | 1312, 103 | 111893, 104 | 111879, 105 | 97, 106 | 111088, 107 | 115514, 108 | 1358, 109 | 1854, 110 | 114833, 111 | 115320, 112 | 1937, 113 | 111099, 114 | 108767, 115 | 45269, 116 | 114602, 117 | 678, 118 | 112568 119 | ], 120 | "G310": [ 121 | 1978, 122 | 1629, 123 | 8023, 124 | 1727, 125 | 91467, 126 | 31247, 127 | 113926, 128 | 566, 129 | 1748, 130 | 4418, 131 | 537, 132 | 529, 133 | 1348, 134 | 12092, 135 | 1218, 136 | 10436, 137 | 109118, 138 | 278, 139 | 1226 140 | ], 141 | "G321": [ 142 | 1978, 143 | 1629, 144 | 8023, 145 | 1727, 146 | 91467, 147 | 31247, 148 | 113926, 149 | 566, 150 | 1748, 151 | 4418, 152 | 537, 153 | 529, 154 | 1348, 155 | 1704, 156 | 1226 157 | ], 158 | "G350": [ 159 | 12092, 160 | 1218, 161 | 10436, 162 | 109118, 163 | 278, 164 | 1226, 165 | 566, 166 | 1748, 167 | 4418, 168 | 537, 169 | 529, 170 | 1348, 171 | 115352, 172 | 114514, 173 | 116116, 174 | 109112, 175 | 105014 176 | ], 177 | "G363": [ 178 | 12092, 179 | 1218, 180 | 10436, 181 | 109118, 182 | 278, 183 | 115352, 184 | 114514, 185 | 116116, 186 | 109112, 187 | 105014, 188 | 2606, 189 | 1721, 190 | 115836, 191 | 107241, 192 | 109328, 193 | 1355, 194 | 1244, 195 | 1270, 196 | 90742, 197 | 1411, 198 | 108443, 199 | 2210, 200 | 109122 201 | ], 202 | "G375": [ 203 | 95937, 204 | 116294, 205 | 98653, 206 | 116191, 207 | 115352, 208 | 114514, 209 | 116116, 210 | 109112, 211 | 105014, 212 | 2606, 213 | 1721, 214 | 115836, 215 | 107241, 216 | 109328, 217 | 1355, 218 | 1244, 219 | 1270, 220 | 90742, 221 | 1411, 222 | 108443, 223 | 2210, 224 | 109122 225 | ], 226 | "G379": [ 227 | 95937, 228 | 116294, 229 | 98653, 230 | 116191, 231 | 115352, 232 | 114514, 233 | 116116, 234 | 109112, 235 | 105014, 236 | 12092, 237 | 1218, 238 | 10436, 239 | 1270, 240 | 90742, 241 | 1411, 242 | 1637, 243 | 113568, 244 | 43877, 245 | 108443, 246 | 2210, 247 | 109122 248 | ], 249 | "G236": [ 250 | "45269", 251 | "4866", 252 | "1312", 253 | "108767", 254 | "111099", 255 | "97", 256 | "111879", 257 | "1854", 258 | "12111", 259 | "1937", 260 | "3778", 261 | "115514", 262 | "115320", 263 | "1358", 264 | "111088", 265 | "114833", 266 | "111893", 267 | "stories/377", 268 | "stories/256", 269 | "stories/352", 270 | "stories/369", 271 | "stories/371", 272 | "stories/246", 273 | "stories/249", 274 | "stories/373" 275 | ], 276 | "G250": [ 277 | "45269", 278 | "4866", 279 | "1312", 280 | "108767", 281 | "111099", 282 | "97", 283 | "111879", 284 | "1854", 285 | "12111", 286 | "1937", 287 | "3778", 288 | "115514", 289 | "115320", 290 | "1358", 291 | "111088", 292 | "114833", 293 | "111893", 294 | "stories/377", 295 | "stories/256", 296 | "stories/352", 297 | "stories/369", 298 | "stories/371", 299 | "stories/246", 300 | "stories/249", 301 | "stories/373" 302 | ], 303 | "G254": [ 304 | "45269", 305 | "4866", 306 | "1312", 307 | "108767", 308 | "111099", 309 | "97", 310 | "111879", 311 | "1854", 312 | "12111", 313 | "1937", 314 | "3778", 315 | "115514", 316 | "115320", 317 | "1358", 318 | "111088", 319 | "114833", 320 | "111893", 321 | "stories/377", 322 | "stories/256", 323 | "stories/352", 324 | "stories/369", 325 | "stories/371", 326 | "stories/246", 327 | "stories/249", 328 | "stories/373" 329 | ], 330 | "G220": [ 331 | 122167, 332 | 122102, 333 | 122502, 334 | 122505, 335 | 3369, 336 | 122189, 337 | 122166, 338 | 122492, 339 | 122113, 340 | 122115, 341 | 6612 342 | ], 343 | "G223": [ 344 | 5788, 345 | 116725, 346 | 108860, 347 | 60728, 348 | 4324, 349 | 1380, 350 | 4379, 351 | 117153, 352 | 118304, 353 | 7505, 354 | 22412, 355 | 114429, 356 | 376, 357 | 4829 358 | ], 359 | "G225": [ 360 | 122167, 361 | 122102, 362 | 122502, 363 | 122505, 364 | 3369, 365 | 122189, 366 | 122166, 367 | 122492, 368 | 122113, 369 | 122115, 370 | 6612 371 | ], 372 | "G237": [ 373 | 122167, 374 | 122102, 375 | 122502, 376 | 122505, 377 | 3369, 378 | 122189, 379 | 122166, 380 | 122492, 381 | 122113, 382 | 122115, 383 | 6612 384 | ], 385 | } 386 | -------------------------------------------------------------------------------- /js/vendor/imagesloaded.pkgd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * imagesLoaded PACKAGED v3.1.7 3 | * JavaScript is all like "You images are done yet or what?" 4 | * MIT License 5 | */ 6 | 7 | (function(){function e(){}function t(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var i=e.prototype,r=this,o=r.EventEmitter;i.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},i.flattenListeners=function(e){var t,n=[];for(t=0;e.length>t;t+=1)n.push(e[t].listener);return n},i.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},i.addListener=function(e,n){var i,r=this.getListenersAsObject(e),o="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(o?n:{listener:n,once:!1});return this},i.on=n("addListener"),i.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},i.once=n("addOnceListener"),i.defineEvent=function(e){return this.getListeners(e),this},i.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},i.removeListener=function(e,n){var i,r,o=this.getListenersAsObject(e);for(r in o)o.hasOwnProperty(r)&&(i=t(o[r],n),-1!==i&&o[r].splice(i,1));return this},i.off=n("removeListener"),i.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},i.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},i.manipulateListeners=function(e,t,n){var i,r,o=e?this.removeListener:this.addListener,s=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)o.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?o.call(this,i,r):s.call(this,i,r));return this},i.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},i.removeAllListeners=n("removeEvent"),i.emitEvent=function(e,t){var n,i,r,o,s=this.getListenersAsObject(e);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].length;i--;)n=s[r][i],n.once===!0&&this.removeListener(e,n.listener),o=n.listener.apply(this,t||[]),o===this._getOnceReturnValue()&&this.removeListener(e,n.listener);return this},i.trigger=n("emitEvent"),i.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},i.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},i._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},i._getEvents=function(){return this._events||(this._events={})},e.noConflict=function(){return r.EventEmitter=o,e},"function"==typeof define&&define.amd?define("eventEmitter/EventEmitter",[],function(){return e}):"object"==typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){function t(t){var n=e.event;return n.target=n.target||n.srcElement||t,n}var n=document.documentElement,i=function(){};n.addEventListener?i=function(e,t,n){e.addEventListener(t,n,!1)}:n.attachEvent&&(i=function(e,n,i){e[n+i]=i.handleEvent?function(){var n=t(e);i.handleEvent.call(i,n)}:function(){var n=t(e);i.call(e,n)},e.attachEvent("on"+n,e[n+i])});var r=function(){};n.removeEventListener?r=function(e,t,n){e.removeEventListener(t,n,!1)}:n.detachEvent&&(r=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var o={bind:i,unbind:r};"function"==typeof define&&define.amd?define("eventie/eventie",o):e.eventie=o}(this),function(e,t){"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],function(n,i){return t(e,n,i)}):"object"==typeof exports?module.exports=t(e,require("eventEmitter"),require("eventie")):e.imagesLoaded=t(e,e.EventEmitter,e.eventie)}(window,function(e,t,n){function i(e,t){for(var n in t)e[n]=t[n];return e}function r(e){return"[object Array]"===d.call(e)}function o(e){var t=[];if(r(e))t=e;else if("number"==typeof e.length)for(var n=0,i=e.length;i>n;n++)t.push(e[n]);else t.push(e);return t}function s(e,t,n){if(!(this instanceof s))return new s(e,t);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=o(e),this.options=i({},this.options),"function"==typeof t?n=t:i(this.options,t),n&&this.on("always",n),this.getImages(),a&&(this.jqDeferred=new a.Deferred);var r=this;setTimeout(function(){r.check()})}function c(e){this.img=e}function f(e){this.src=e,v[e]=this}var a=e.jQuery,u=e.console,h=u!==void 0,d=Object.prototype.toString;s.prototype=new t,s.prototype.options={},s.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);var i=n.nodeType;if(i&&(1===i||9===i||11===i))for(var r=n.querySelectorAll("img"),o=0,s=r.length;s>o;o++){var c=r[o];this.addImage(c)}}},s.prototype.addImage=function(e){var t=new c(e);this.images.push(t)},s.prototype.check=function(){function e(e,r){return t.options.debug&&h&&u.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var o=this.images[r];o.on("confirm",e),o.check()}},s.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded;var t=this;setTimeout(function(){t.emit("progress",t,e),t.jqDeferred&&t.jqDeferred.notify&&t.jqDeferred.notify(t,e)})},s.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";this.isComplete=!0;var t=this;setTimeout(function(){if(t.emit(e,t),t.emit("always",t),t.jqDeferred){var n=t.hasAnyBroken?"reject":"resolve";t.jqDeferred[n](t)}})},a&&(a.fn.imagesLoaded=function(e,t){var n=new s(this,e,t);return n.jqDeferred.promise(a(this))}),c.prototype=new t,c.prototype.check=function(){var e=v[this.img.src]||new f(this.img.src);if(e.isConfirmed)return this.confirm(e.isLoaded,"cached was confirmed"),void 0;if(this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this;e.on("confirm",function(e,n){return t.confirm(e.isLoaded,n),!0}),e.check()},c.prototype.confirm=function(e,t){this.isLoaded=e,this.emit("confirm",this,t)};var v={};return f.prototype=new t,f.prototype.check=function(){if(!this.isChecked){var e=new Image;n.bind(e,"load",this),n.bind(e,"error",this),e.src=this.src,this.isChecked=!0}},f.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},f.prototype.onload=function(e){this.confirm(!0,"onload"),this.unbindProxyEvents(e)},f.prototype.onerror=function(e){this.confirm(!1,"onerror"),this.unbindProxyEvents(e)},f.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit("confirm",this,t)},f.prototype.unbindProxyEvents=function(e){n.unbind(e.target,"load",this),n.unbind(e.target,"error",this)},s}); -------------------------------------------------------------------------------- /fonts/fontello/font/griot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2014 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /js/vendor/angular-touch.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"angular-touch.min.js", 4 | "lineCount":12, 5 | "mappings":"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", 6 | "sources":["angular-touch.js"], 7 | "names":["window","angular","undefined","makeSwipeDirective","directiveName","direction","eventName","ngTouch","directive","$parse","$swipe","MAX_VERTICAL_DISTANCE","MAX_VERTICAL_RATIO","MIN_HORIZONTAL_DISTANCE","scope","element","attr","validSwipe","coords","startCoords","deltaY","Math","abs","y","deltaX","x","valid","swipeHandler","bind","start","event","cancel","end","$apply","triggerHandler","module","factory","getCoordinates","touches","length","e","changedTouches","originalEvent","clientX","clientY","eventHandlers","totalX","totalY","lastPos","active","on","MOVE_BUFFER_RADIUS","preventDefault","config","$provide","decorator","$delegate","shift","$timeout","$rootElement","checkAllowableRegions","touchCoordinates","i","x1","CLICKBUSTER_THRESHOLD","y1","splice","onClick","Date","now","lastPreventedTime","PREVENT_DURATION","lastLabelClickCoordinates","target","tagName","toLowerCase","stopPropagation","blur","onTouchStart","push","ACTIVE_CLASS_NAME","resetState","tapping","removeClass","clickHandler","ngClick","tapElement","startTime","touchStartX","touchStartY","srcElement","nodeType","parentNode","addClass","diff","dist","sqrt","pow","TAP_DURATION","MOVE_TOLERANCE","addEventListener","isDefined","disabled","onclick","element.onclick","touchend"] 8 | } 9 | -------------------------------------------------------------------------------- /js/controllers/object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller for object template. 3 | */ 4 | 5 | app.controller('ObjectCtrl', ['$scope', '$routeParams', '$location', '$sce', 'resolvedNotes', 'segmentio', '$rootScope', 'miaMediaMetaAdapter', 'miaObjectMetaAdapter', 'miaThumbnailAdapter', 'email', 'envConfig', '$timeout', 'resolvedObjectMeta', 6 | function($scope, $routeParams, $location, $sce, notes, segmentio, $rootScope, mediaMeta, objectMetaAdapter, miaThumbs, email, config, $timeout, objectMeta) { 7 | // Defaults 8 | $scope.movedZoomer = false; 9 | $scope.currentAttachment = null; 10 | $scope.contentMinimized = window.outerWidth < 1024; 11 | $scope.enableSharing = config.miaEmailSharingActive 12 | $scope.translucent = false; 13 | 14 | $scope.id = $routeParams.id 15 | $rootScope.lastObjectId = $scope.id = $routeParams.id 16 | 17 | _wp = notes 18 | $scope.wp = _wp.objects[$scope.id] 19 | segmentio.track('Browsed an Object', {id: $scope.id, name: $scope.wp.title}) 20 | 21 | $scope.wp.meta3 = $sce.trustAsHtml( $scope.wp.meta3 ); 22 | 23 | // Replace object metadata if using adapter 24 | if( objectMetaAdapter.isActive ) { 25 | $scope.wp.meta1 = $scope.wp.meta1 || objectMeta.meta1; 26 | $scope.wp.meta2 = $scope.wp.meta2 || objectMeta.meta2; 27 | $scope.wp.meta3 = $scope.wp.meta3 || objectMeta.meta3; 28 | $scope.wp.location = objectMeta.location; 29 | } 30 | 31 | $scope.relatedStories = [] 32 | angular.forEach($scope.wp.relatedStories, function(story_id){ 33 | if( _wp.stories[story_id] ) { 34 | $scope.relatedStories.push({ 35 | 'id':story_id, 36 | 'title':_wp.stories[story_id].title 37 | }) 38 | } 39 | }) 40 | if($scope.wp) { 41 | $scope.mainImage = $scope.wp.views[0].image 42 | $scope.wp.trustedDescription = $sce.trustAsHtml($scope.wp.description) 43 | $scope.$on('viewChanged', loadDetails) 44 | $scope.$watch('mapLoaded', function(v) { v && loadDetails() }) // TODO: once details load, cancel this watch 45 | 46 | // Open the More tab when returning from a story via the 'Back' button 47 | if($rootScope.nextView) { 48 | $scope.activeSection = $rootScope.nextView 49 | $rootScope.nextView = undefined 50 | // make sure the drawer is open, if we have one 51 | var drawer = angular.element($('.object-content-container')).scope().drawerify 52 | if(drawer) drawer.to('open', 0) 53 | } 54 | $scope.$$phase || $scope.$apply() 55 | 56 | } 57 | 58 | var loadDetails = function() { 59 | $scope.notes = $scope.wp.views; 60 | $scope.allNotes = []; 61 | $scope.allAttachments = []; 62 | angular.forEach($scope.notes, function(view) { 63 | angular.forEach(view.annotations, function(ann) { 64 | ann.trustedDescription = $sce.trustAsHtml(ann.description) 65 | ann.view = view; 66 | $scope.allNotes.push( ann ); 67 | 68 | // Replace attachment metadata if using adapter 69 | angular.forEach( ann.attachments, function(att) { 70 | att.thumbnail = miaThumbs.get(att.image_id) 71 | if( mediaMeta.isActive ) { 72 | // Hacky! We need to only trustAsHtml(att.meta) once. Or find a better way generally. 73 | att.meta = mediaMeta.get( att.image_id ) || ( typeof att.meta === 'object' ? att.meta : $sce.trustAsHtml(att.meta) ); 74 | } 75 | att.trustedDescription = $sce.trustAsHtml(att.description); 76 | $scope.allAttachments.push( att ); 77 | }) 78 | 79 | }) 80 | }) 81 | $scope.$$phase || $scope.$apply() 82 | } 83 | 84 | $scope.next = function(direction) { 85 | var next = $scope.objects.ids[$scope.objects.ids.indexOf(parseInt($scope.id))+1] 86 | if(next) $location.url('/o/'+next) 87 | } 88 | $scope.prev = function(direction) { 89 | var prev = $scope.objects.ids[$scope.objects.ids.indexOf(parseInt($scope.id))-1] 90 | if(prev) $location.url('/o/'+prev) 91 | } 92 | 93 | $scope.viewEnabled = function(nextView, debug) { 94 | return (nextView == 'more' && $scope.relatedStories && $scope.relatedStories.length > 0 || 95 | nextView == 'annotations' && getFirstDetail() || 96 | nextView == 'about') 97 | } 98 | 99 | // return the first annotation, falsey if there aren't any 100 | function getFirstDetail() { 101 | var i = 0, view = $scope.notes && $scope.notes[i], firstDetail 102 | while(view && view.annotations.length == 0) { 103 | view = $scope.notes && $scope.notes[i] 104 | i++ 105 | } 106 | firstDetail = view && view.annotations && view.annotations[0] 107 | return firstDetail 108 | } 109 | 110 | $scope.toggleView = function(nextView, dontTrack) { 111 | if(!dontTrack) segmentio.track('Changed Sections within an Object', {view: nextView}) 112 | nextView = nextView || 'about' 113 | if(!$scope.viewEnabled(nextView)) return 114 | if(nextView == 'annotations') { 115 | if(!$scope.notes) $scope.notes = $scope.wp.views 116 | var firstDetail = getFirstDetail() 117 | 118 | if(firstDetail && !$scope.flatmapScope.lastActiveNote) { 119 | $scope.activateNote(firstDetail, $scope.notes[0]) 120 | } else if($scope.flatmapScope.lastActiveNote) { 121 | // If there's an active annotation, center the map over it. 122 | $scope.glanceText = $sce.trustAsHtml( "Press to view detail " + $scope.flatmapScope.lastActiveNote.index + "" ); 123 | if(!$scope.flatmapScope.zoom.map.getBounds().contains($scope.flatmapScope.jsonLayer.getBounds())) { 124 | $scope.$broadcast('changeGeometry', $scope.flatmapScope.lastActiveNote.geoJSON.geometry) 125 | } 126 | } 127 | } else { 128 | $scope.glanceText = $sce.trustAsHtml( "Press to view object" ); 129 | } 130 | // Reset image to the primary when changing back to about 131 | if(nextView == 'about' && $scope.flatmapScope && $scope.notes[0].image != $scope.flatmapScope.image) { 132 | $scope.activateView($scope.notes[0]) 133 | } 134 | $scope.activeSection = nextView 135 | } 136 | 137 | $scope.toggleAttachment = function(attachment, closeAttachmentIfOpen, $event){ 138 | if($scope.currentAttachment==attachment){ 139 | if(!closeAttachmentIfOpen) return; 140 | $scope.currentAttachment = $scope.showAttachmentCredits = null; 141 | } else { 142 | $scope.currentAttachment = attachment; 143 | $scope.showAttachmentCredits = false 144 | setTimeout(Zoomer.windowResized, 0); 145 | } 146 | if($event) $event.stopPropagation(); 147 | } 148 | $scope.toggleAttachmentCredits = function(attachment) { 149 | $scope.showAttachmentCredits = !$scope.showAttachmentCredits 150 | setTimeout(Zoomer.windowResized, 125) 151 | } 152 | 153 | $scope.toggleView($scope.activeSection, true) 154 | $scope.$on('showAnnotationsPanel', function(view) { 155 | $scope.activeSection = 'annotations' 156 | }) 157 | 158 | $scope.changeZoomerForViews = function(map, flatmapScope) { 159 | $scope.$apply(function() { $scope.showViews = true }) 160 | } 161 | 162 | $scope.activateNote = function(note, view) { 163 | /* 164 | $scope.translucent = true; 165 | */ 166 | $scope.showViews = false 167 | $scope.activeView = view 168 | note.active = !note.active 169 | $scope.glanceText = $sce.trustAsHtml( "Press to view detail " + note.index + "" ); 170 | /* 171 | $timeout( function(){ 172 | $scope.translucent = false; 173 | }, 1000 ); 174 | */ 175 | } 176 | 177 | $scope.deactivateAllNotes = function() { 178 | angular.forEach($scope.notes, function(view) { 179 | angular.forEach(view.annotations, function(ann) { ann.active = false; }) 180 | }) 181 | $scope.$$phase || $scope.$apply() 182 | } 183 | 184 | $scope.activateView = function(view) { 185 | // TODO: encapsulate active view the same way I do notes, with view.active? 186 | $scope.showViews = false 187 | $scope.activeView = view 188 | $scope.deactivateAllNotes() 189 | 190 | $scope.flatmapScope.$broadcast('changeView', view) 191 | } 192 | $scope.activateViewAndShowFirstAnnotation = function(view) { 193 | $scope.activateView(view) 194 | var note = view.annotations[0] 195 | if(note) activateNote(note) 196 | } 197 | 198 | $scope.toggleSixbar = function(element) { 199 | $scope.sixBarClosed = !$scope.sixBarClosed 200 | setTimeout(Zoomer.windowResized, 0) 201 | } 202 | 203 | $scope.toggleExtendedTombstone = function(event) { 204 | $scope.showExtendedTombstone = !$scope.showExtendedTombstone 205 | $scope.$broadcast( 'recalculateCustomDrawerStates' ); 206 | if(event) event.stopPropagation() 207 | } 208 | 209 | $scope.toggleMinimizeContent = function() { 210 | $scope.contentMinimized = !$scope.contentMinimized; 211 | //setTimeout( Zoomer.windowResized, 125); // Zoomer now stays put behind content 212 | } 213 | 214 | $scope.glanceText = $sce.trustAsHtml( "Press to view object" ); 215 | } 216 | ]) 217 | 218 | -------------------------------------------------------------------------------- /js/vendor/angular-route.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"angular-route.min.js", 4 | "lineCount":13, 5 | "mappings":"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", 6 | "sources":["angular-route.js"], 7 | "names":["window","angular","undefined","ngViewFactory","$route","$anchorScroll","$animate","link","scope","$element","attr","ctrl","$transclude","cleanupLastView","previousElement","remove","currentScope","$destroy","currentElement","leave","update","locals","current","isDefined","$template","newScope","$new","clone","enter","onNgViewEnter","autoScrollExp","$eval","$emit","onloadExp","autoscroll","onload","$on","ngViewFillContentFactory","$compile","$controller","html","contents","controller","$scope","controllerAs","data","children","ngRouteModule","module","provider","$RouteProvider","inherit","parent","extra","extend","pathRegExp","path","opts","insensitive","caseInsensitiveMatch","ret","keys","replace","_","slash","key","option","optional","star","push","regexp","RegExp","routes","when","this.when","route","redirectPath","length","substr","otherwise","this.otherwise","params","$get","$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce","updateRoute","next","parseRoute","last","$$route","equals","pathParams","reloadOnSearch","forceReload","copy","$broadcast","redirectTo","isString","interpolate","search","url","then","resolve","template","templateUrl","forEach","value","get","invoke","isFunction","getTrustedResourceUrl","loadedTemplateUrl","response","all","error","match","m","exec","on","i","len","val","decodeURIComponent","name","string","result","split","segment","segmentMatch","join","reload","$evalAsync","$RouteParamsProvider","this.$get","directive","$inject"] 8 | } 9 | -------------------------------------------------------------------------------- /fonts/fontello/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 231 | 232 | 233 | 245 | 246 | 247 |
    248 |

    249 | griot 250 | font demo 251 |

    252 | 255 |
    256 |
    257 |
    258 |
    icon-resize-full0xe800
    259 |
    icon-resize-normal0xe801
    260 |
    icon-home0xe802
    261 |
    icon-menu0xe803
    262 |
    263 |
    264 |
    icon-left-open0xe804
    265 |
    icon-right-open0xe805
    266 |
    icon-left0xe806
    267 |
    icon-right0xe807
    268 |
    269 |
    270 |
    icon-left-small0xe808
    271 |
    icon-right-small0xe809
    272 |
    icon-play0xe80a
    273 |
    icon-info-circled0xe80b
    274 |
    275 |
    276 |
    icon-info-circled-alt0xe80c
    277 |
    icon-cancel-circled0xe80d
    278 |
    icon-cancel-circled-outline0xe80e
    279 |
    icon-cancel0xe80f
    280 |
    281 |
    282 |
    icon-cancel-outline0xe810
    283 |
    icon-pause0xe811
    284 |
    icon-back0xe812
    285 |
    icon-info0xe813
    286 |
    287 |
    288 |
    icon-doc-text0xe814
    289 |
    icon-plus0xe815
    290 |
    icon-minus0xe816
    291 |
    icon-resize-vertical0xe817
    292 |
    293 |
    294 |
    icon-mail0xe818
    295 |
    296 |
    297 | 298 | 299 | -------------------------------------------------------------------------------- /sass/object.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Object template styles. 3 | */ 4 | 5 | $sidebarWidth: 23rem; 6 | 7 | /** 8 | * Content - "Above the Fold" (Controls and Meta) 9 | */ 10 | .object-content-container{ 11 | position:absolute; 12 | top:0; 13 | left:0; 14 | width: $sidebarWidth; 15 | height:100%; 16 | } 17 | .object-content{ 18 | @include transition( opacity 500ms ease ); 19 | position:absolute; 20 | top:0; 21 | right:0; 22 | bottom:0; 23 | left:0; 24 | margin:0 auto; 25 | background-color:#ededed; 26 | padding:15px; 27 | overflow-x:hidden; 28 | overflow-y:auto; 29 | 30 | &.translucent{ 31 | opacity:0; 32 | } 33 | 34 | } 35 | 36 | .object-menu { 37 | display:block; 38 | height:40px; 39 | margin:0; 40 | padding:0; 41 | } 42 | .object-menu > a { 43 | position:relative; 44 | display:block; 45 | float:left; 46 | margin:0 5px 0 0; 47 | } 48 | .object-home-link { 49 | @extend .home-icon; 50 | } 51 | .contentMinimized .object-maximize-content { 52 | background-color: rgba(22, 22, 22, .55); 53 | } 54 | 55 | .object-menu .toggle-meta { 56 | @extend .meta-icon; 57 | } 58 | .object-menu .email { 59 | @extend .meta-icon; 60 | @include icon( '\e818' ); 61 | font-size: 21px; 62 | } 63 | 64 | .object-title{ 65 | margin:5px 0 0; 66 | } 67 | .meta1{ 68 | max-height:0; 69 | margin:0; 70 | overflow:hidden; 71 | 72 | .extended & { 73 | max-height:200px; 74 | } 75 | 76 | @media #{$medium} { 77 | @include transition( max-height 125ms ease ); 78 | max-height:none; 79 | } 80 | } 81 | .meta2, 82 | .meta3{ 83 | margin:0; 84 | font-size:0.9em; 85 | font-weight:normal; 86 | max-height:0; 87 | overflow:hidden; 88 | 89 | .extended & { 90 | max-height:225px; 91 | } 92 | 93 | @media #{$medium} { 94 | @include transition( max-height 125ms ease ); 95 | } 96 | } 97 | 98 | 99 | /** 100 | * Navbar 101 | */ 102 | .object-nav { 103 | margin:15px 0 0; 104 | } 105 | .object-nav a { 106 | display:block; 107 | width:33.3%; 108 | float:left; 109 | padding:0.5em 0; 110 | border:1px solid #222; 111 | border-right:none; 112 | color:#222; 113 | text-transform: uppercase; 114 | font-size:0.85em; 115 | font-weight:700; 116 | text-align:center; 117 | background-color: #ededed; 118 | cursor:pointer; 119 | 120 | &.disabled, 121 | &.disabled:hover { 122 | color:#bbb; 123 | background-color:#ededed; 124 | cursor:default; 125 | } 126 | 127 | &:last-child{ 128 | border-right:1px solid #222; 129 | } 130 | 131 | &.selected { 132 | color:#ededed; 133 | background-color: #222; 134 | } 135 | 136 | @media #{$medium} { 137 | &:active{ 138 | color:#ededed; 139 | background-color: #222; 140 | } 141 | } 142 | } 143 | 144 | 145 | /** 146 | * Content - Container 147 | */ 148 | .object-stories{ 149 | margin:0; 150 | padding:0; 151 | } 152 | 153 | 154 | /** 155 | * Content - Details/Annotations 156 | */ 157 | .object-details{ 158 | list-style:none; 159 | padding:0; 160 | } 161 | .annotation{ 162 | padding:0 0 15px 0; 163 | position:relative; 164 | } 165 | .annotation-title{ 166 | margin:0 0 0 40px; 167 | cursor:pointer; 168 | } 169 | .annotation-title:active { 170 | text-decoration: underline; 171 | } 172 | .annotation-index{ 173 | position:absolute; 174 | display:block; 175 | top:0; 176 | left:0; 177 | width:30px; 178 | height:30px; 179 | margin-right:10px; 180 | color:#fff; 181 | background-color:#222; 182 | border-radius:1000px; 183 | border:2px solid #fff; 184 | text-align:center; 185 | line-height:26px; 186 | font-size:1.125rem; 187 | 188 | .glance &{ 189 | position:relative; 190 | display:inline-block; 191 | margin:0; 192 | } 193 | } 194 | .annotation-content{ 195 | @include transition( max-height 125ms ease ); 196 | max-height:0; 197 | overflow:hidden; 198 | 199 | .annotation.active &{ 200 | max-height:500px; 201 | } 202 | } 203 | .annotation-content p{ 204 | margin:10px 0 0 0; 205 | } 206 | 207 | .annotation-content ul { 208 | margin-top: 10px; 209 | } 210 | .annotation-content ul li { 211 | list-style-type: disc; 212 | margin-left: -1.25em; 213 | } 214 | 215 | /** 216 | * Content - Details/Annotations - Attachments 217 | */ 218 | .annotation-attachments{ 219 | @include transition( max-height 125ms ease ); 220 | max-height:0; 221 | overflow:hidden; 222 | padding:0; 223 | margin:0; 224 | 225 | .annotation.active &{ 226 | max-height:500px; 227 | } 228 | } 229 | .annotation-attachments p{ 230 | font-size:0.75em; 231 | margin:15px 0 10px; 232 | text-transform:uppercase; 233 | color:#999; 234 | font-weight:bold; 235 | border-top:1px solid #ccc; 236 | } 237 | .attachment-thumb{ 238 | display:inline-block; 239 | width:75px; 240 | height:75px; 241 | position:relative; 242 | margin:0 5px 5px 0; 243 | cursor:pointer; 244 | background-size:cover; 245 | } 246 | .attachment-big{ 247 | @include transition( left 375ms ease ); 248 | display:block; 249 | position:absolute; 250 | width:100%; 251 | height:100%; 252 | left:-300%; 253 | top:0; 254 | background-color:#ddd; 255 | z-index:1000000001; 256 | text-align:center; 257 | 258 | &.active { 259 | left:0; 260 | } 261 | } 262 | .attachment-zoomer{ 263 | @include transition( height 125ms ease ); 264 | height:100%; 265 | 266 | .showAttachmentMeta & { 267 | height:67%; 268 | } 269 | } 270 | .attachment-big .return-link{ 271 | @extend .nav-button; 272 | @extend .nav-button-left; 273 | @extend .return-icon; 274 | position:absolute; 275 | border-top:1px solid #222; 276 | border-right:1px solid #222; 277 | } 278 | .attachment-toggle-meta{ 279 | @extend .meta-icon; 280 | position:absolute; 281 | display:block; 282 | right:12px; 283 | bottom:8px; 284 | margin:0; 285 | z-index:4; 286 | } 287 | .attachment-tombstone{ 288 | @include transition( bottom 125ms ease ); 289 | @include box-shadow( inset 0 5px 5px rgba(0,0,0,0.23)); 290 | position:absolute; 291 | right:0; 292 | bottom:-33%; 293 | left:0; 294 | height:33%; 295 | padding:15px 60px; 296 | overflow-y:scroll; 297 | overflow-x:hidden; 298 | font-size:0.9em; 299 | text-align:left; 300 | background-color:#ededed; 301 | color:#444; 302 | 303 | .showAttachmentMeta & { 304 | bottom:0; 305 | } 306 | } 307 | .attachment-content{ 308 | 309 | } 310 | 311 | 312 | /** 313 | * Content - Stories/"More" 314 | */ 315 | .more-title { 316 | margin:15px 0 0; 317 | text-transform:uppercase; 318 | } 319 | 320 | .object-related{ 321 | list-style:none; 322 | padding:0; 323 | } 324 | .object-related a{ 325 | color:#222; 326 | width:100%; 327 | border:1px solid #222; 328 | padding:10px 15px; 329 | display:block; 330 | margin:5px 0px; 331 | text-align:left; 332 | font-size:1em; 333 | transition:all 250ms linear; 334 | text-decoration: none; 335 | 336 | @media screen and (min-width:1025px) { 337 | &:hover { 338 | color:#ededed; 339 | background-color:#222; 340 | } 341 | } 342 | &:active { 343 | color:#ededed; 344 | background-color:#222; 345 | } 346 | } 347 | 348 | 349 | /** 350 | * Zoomer 351 | */ 352 | .object-zoomer { 353 | @include box-shadow( 0px 3px 5px rgba(0,0,0,0.23) ); 354 | @include transition( opacity 125ms ease ); 355 | position:absolute; 356 | top:0; 357 | right:0; 358 | bottom:0; 359 | left:0; 360 | background-color:#ccc; 361 | text-align:center; 362 | 363 | @media #{$medium} { 364 | left: $sidebarWidth; 365 | } 366 | } 367 | 368 | 369 | /* 370 | * Zoomer - Markers 371 | */ 372 | .leaflet-overlay-pane, 373 | .leaflet-marker-pane{ 374 | display:none; 375 | 376 | .object-zoomer.annotations & { 377 | display:block; 378 | } 379 | } 380 | .noteMarker { 381 | @include transition( all 125ms ease ); 382 | padding: 3em; 383 | margin: -3em !important; 384 | text-align: center; 385 | font-family: 'MiaGrotesk-Light', sans-serif; 386 | font-weight: bold; 387 | font-size: 1em ; 388 | color: #fff; 389 | z-index:1000000 !important; 390 | } 391 | .noteMarker span { 392 | @include transition( all 125ms ease ); 393 | display: block; 394 | height: 30px; 395 | width: 30px; 396 | margin: -15px 0 0 -15px; 397 | padding: 0; 398 | border-radius: 1000px; 399 | border: 2px solid #fff; 400 | font-weight: bold; 401 | text-align: center; 402 | font-size: 1.125rem; 403 | background-color: rgba(22, 22, 22, .55); 404 | line-height:26px; 405 | vertical-align:middle; 406 | } 407 | .noteMarker:hover span, .noteMarker:active span { 408 | width:36px; 409 | height:36px; 410 | margin: -18px 0 0 -18px; 411 | line-height:34px; 412 | } 413 | 414 | // Toggle 'show all views' control 415 | .object-zoomer .flatmap > div:first-child { 416 | z-index: 100; 417 | width: 100%; 418 | margin: 0; 419 | padding: 0; 420 | } 421 | .object-zoomer .flatmap .view { 422 | margin-left: -100em; 423 | @include transition(margin 125ms ease-out); 424 | } 425 | .showAllViews .object-zoomer .flatmap .view { 426 | display: inline; 427 | min-height: 3em; 428 | margin-left: 0; 429 | @include transition(margin 125ms ease); 430 | } 431 | .object-zoomer aside { 432 | display: none; 433 | font-size: 1.25em; 434 | font-weight: bold; 435 | } 436 | .showAllViews aside { 437 | display: block !important; 438 | } 439 | 440 | // Transition the leaflet pane away 441 | .object-zoomer .flatmap .leaflet-map-pane { 442 | @include transition(-webkit-transform 125ms ease-out); 443 | } 444 | .showAllViews .object-zoomer .flatmap .leaflet-map-pane { 445 | -webkit-transform: scale(0.25) translateX(-1234px) !important; 446 | } 447 | 448 | .more-views a, a.view { 449 | height: 8em; 450 | width: 32.8%; 451 | margin: 0.5% 0.5% 0 0; 452 | background-size: cover; 453 | background-position: 50% 15%; 454 | display: block; 455 | float: left; 456 | // margin-bottom: 1px; 457 | 458 | &.open, &:hover { 459 | border: 5px solid rgba(255, 255, 255, 0.6); 460 | } 461 | } 462 | .object-zoomer .flatmap a.view { 463 | height: 17em; 464 | } 465 | .leaflet-control-container *{ 466 | @include transition(opacity 125ms ease-out); 467 | } 468 | .showAllViews .object-zoomer .leaflet-control-container .leaflet-right { 469 | opacity: 0; 470 | } 471 | 472 | // Email share form 473 | #share.email form { 474 | margin-top: -0.5em; 475 | max-width: 0; 476 | opacity: 0; 477 | margin-left: 2.5em; 478 | -webkit-transition: all 125ms ease-out; 479 | } 480 | 481 | #share.email.open form { 482 | position: relative; 483 | z-index: 2; 484 | max-width: 33em; 485 | opacity: 1; 486 | } 487 | 488 | 489 | // Hint 490 | .hint-scale{ 491 | @include transition( all 300ms ease ); 492 | @include transform( scale(1.25) ); 493 | position:absolute; 494 | top:50%; 495 | left:50%; 496 | width:250px; 497 | height:250px; 498 | margin-top:-125px; 499 | margin-left:-125px; 500 | padding:25px; 501 | background-color:rgba(0,0,0,0.6); 502 | border-radius:25px; 503 | border:3px solid #fff; 504 | z-index:1; 505 | opacity:0; 506 | 507 | &.visible{ 508 | @include transform( scale(1) ); 509 | opacity:1; 510 | } 511 | } 512 | 513 | .object-about .galleryLocation { 514 | text-align: center; 515 | border-top: 1px solid #ccc; 516 | margin-top: -0.5em; 517 | padding-top: 0.75em; 518 | } 519 | .galleryLocation img { 520 | border: 1px solid #ccc; 521 | max-width: 279px; 522 | } 523 | .galleryLocation img + p { 524 | margin-top: 0.25em; 525 | } 526 | -------------------------------------------------------------------------------- /sass/generic.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Site and reusable element styles. 3 | */ 4 | 5 | *, 6 | *:before, 7 | *:after { 8 | @include box-sizing( border-box ); 9 | } 10 | 11 | 12 | html, 13 | body { 14 | position:fixed; 15 | width:100%; 16 | height:100%; 17 | margin: 0; 18 | padding: 0; 19 | font-size: 100%; 20 | text-rendering:optimizeLegibility; 21 | -moz-font-feature-settings:"kern=0"; 22 | -webkit-font-feature-settings:"kern" 0; 23 | -o-font-feature-settings:"kern" 0; 24 | -ms-font-feature-settings:"kern" 0; 25 | font-feature-settings:"kern" 0; 26 | -webkit-text-size-adjust: 100%; /* Prevent font scaling in landscape while allowing user zoom */ 27 | } 28 | 29 | /* Container styles */ 30 | .container { 31 | @include transition( opacity 150ms ease ); 32 | position:absolute; 33 | top:0; 34 | left:0; 35 | width:100%; 36 | height:100%; 37 | overflow:hidden; 38 | } 39 | 40 | /* Fluid images */ 41 | 42 | img, 43 | object { 44 | max-width: 100%; 45 | outline:0; 46 | border:0; 47 | margin:0; 48 | padding:0; 49 | } 50 | 51 | /* Remove link highlighting */ 52 | a { 53 | -webkit-tap-highlight-color: transparent; 54 | } 55 | 56 | /* Clearfix */ 57 | 58 | .clearfix:after { 59 | content: "."; 60 | visibility: hidden; 61 | display: block; 62 | height: 0; 63 | clear: both; 64 | } 65 | 66 | * html .clearfix { zoom: 1; } /* IE6 */ 67 | *:first-child+html .clearfix { zoom: 1; } /* IE7 */ 68 | 69 | /* Vertical centering */ 70 | 71 | .vcenter-table { 72 | display:table; 73 | width:100%; 74 | height:100%; 75 | } 76 | .vcenter-cell { 77 | display:table-cell; 78 | vertical-align:middle; 79 | } 80 | 81 | /* Type */ 82 | 83 | html, 84 | body{ 85 | font-family: 'MiaGrotesk-Light', sans-serif; 86 | line-height:1.4; 87 | } 88 | 89 | h1 { font-size: 1.6875em; } 90 | h2 { font-size: 1.375em; } 91 | h3 { font-size: 1.375em; } 92 | h4 { font-size: 1.125em; } 93 | h5 { font-size: 1.125em; } 94 | h6 { font-size: 1em; } 95 | 96 | /* Colors */ 97 | 98 | html, 99 | body{ 100 | background-color:#ededed; 101 | } 102 | 103 | /* Flatmaps */ 104 | 105 | .flatmap { 106 | position:absolute; 107 | top:0; 108 | right:0; 109 | bottom:0; 110 | left:0; 111 | } 112 | .flatmap .hint { 113 | @include transition( opacity 250ms linear ); 114 | position: absolute; 115 | right:15px; 116 | bottom:15px; 117 | margin: 0; 118 | padding: 10px; 119 | color: #444; 120 | background: rgba(255, 255, 255, 0.75); 121 | font-style:italic; 122 | white-space:nowrap; 123 | opacity:1; 124 | z-index: 3; 125 | 126 | // Override Leaflet font styles 127 | font-family: 'MiaGrotesk-Light', sans-serif !important; 128 | font-size: 16px !important; 129 | line-height:1.4 !important; 130 | 131 | // TODO: Target non-touch devices 132 | @media screen and (min-width:1025px) { 133 | display:none; 134 | } 135 | 136 | } 137 | .flatmap.zoomed .hint { 138 | opacity: 0; 139 | } 140 | 141 | /** 142 | * Button and Icon styles 143 | * 144 | * @extend these and define positioning and special styling. 145 | */ 146 | 147 | .home-icon{ 148 | @include icon('\e802'); 149 | width:40px; 150 | height:40px; 151 | background-color:#222; 152 | border-radius:1000px; 153 | border:2px solid #fff; 154 | cursor:pointer; 155 | 156 | &:before { 157 | color:#fff; 158 | font-size:18px; 159 | margin-top:-9px; 160 | } 161 | } 162 | 163 | .minimize-icon { 164 | @include icon( '\e801' ); 165 | @include transition( margin-top 100ms linear ); 166 | width:40px; 167 | height:40px; 168 | border-radius:1000px; 169 | background-color:#222; 170 | border:2px solid #fff; 171 | cursor:pointer; 172 | 173 | &:before{ 174 | color:#fff; 175 | font-size:20px; 176 | margin-top:-10px; 177 | } 178 | 179 | .contentMinimized &{ 180 | @include icon( '\e800' ); 181 | 182 | &:before{ 183 | font-size:20px; 184 | margin-top:-10px; 185 | } 186 | } 187 | } 188 | 189 | .maximize-icon { 190 | @include icon( '\e800' ); 191 | width:40px; 192 | height:40px; 193 | border-radius:1000px; 194 | background-color:#222; 195 | border:2px solid #fff; 196 | cursor:pointer; 197 | 198 | &:before{ 199 | color:#fff; 200 | font-size:20px; 201 | margin-top:-10px; 202 | } 203 | 204 | } 205 | 206 | .meta-icon { 207 | @include icon( '\e813' ); 208 | @include transition( all 125ms ease ); 209 | width:40px; 210 | height:40px; 211 | background-color:#222; 212 | border-radius:1000px; 213 | border:2px solid #fff; 214 | 215 | // Icon 216 | &:before{ 217 | color:#fff; 218 | font-size:20px; 219 | margin-top:-10px; 220 | } 221 | 222 | &.open, 223 | .showAttachmentMeta &{ 224 | background-color:#fff; 225 | border-color:#222; 226 | 227 | // Icon 228 | &:before{ 229 | color:#222; 230 | } 231 | } 232 | } 233 | 234 | .home-icon, .minimize-icon, .maximize-icon, .meta-icon { 235 | &:hover, &:active { 236 | border: 2px solid #222; 237 | } 238 | } 239 | 240 | /** 241 | * Navigation 242 | */ 243 | .nav-button { 244 | position:fixed; 245 | margin-top:0; 246 | padding:10px 5px 5px; 247 | background-color:#222; 248 | color:#fff; 249 | font-size:0.7em; 250 | text-transform:uppercase; 251 | text-align:center; 252 | cursor:pointer; 253 | z-index:3; 254 | 255 | // -- rev -- // 256 | // top:50%; 257 | // width:45px; 258 | // height:45px; 259 | padding:0; 260 | width:70px; 261 | height:40px; 262 | border-width:0; 263 | 264 | // Icon 265 | &:before{ 266 | top:50%; 267 | font-size:24px; 268 | margin-top:-12px; 269 | } 270 | 271 | @media #{$medium} { 272 | top:50%; 273 | width:60px; 274 | height:80px; 275 | margin-top:-40px; 276 | border:1px solid #222; 277 | 278 | &:before{ 279 | margin-top:-24px; 280 | } 281 | 282 | &:after{ 283 | position:relative; 284 | display:block; 285 | top:50%; 286 | width:80%; 287 | margin:5px auto 0; 288 | text-align:center; 289 | line-height:12px; 290 | } 291 | } 292 | } 293 | .nav-button-left { 294 | left:0; 295 | bottom:0; 296 | border-left:none; 297 | } 298 | .nav-button-right { 299 | right:0; 300 | bottom:0; 301 | border-right:none; 302 | } 303 | .prev-icon { 304 | @include icon( '\e808' ); 305 | 306 | // Icon 307 | &:before{ 308 | // -- rev -- // 309 | /* 310 | top:auto; 311 | bottom:10px; 312 | left:0; 313 | */ 314 | } 315 | 316 | @media #{$medium} { 317 | // Text 318 | &:after{ 319 | content:'Prev Page'; 320 | } 321 | } 322 | } 323 | .next-icon { 324 | @include icon( '\e809' ); 325 | 326 | // Icon 327 | &:before{ 328 | // -- rev -- // 329 | /* 330 | top:auto; 331 | bottom:10px; 332 | left:0; 333 | */ 334 | } 335 | 336 | @media #{$medium} { 337 | // Text 338 | &:after{ 339 | content:'Next Page'; 340 | } 341 | } 342 | } 343 | .return-icon { 344 | @include icon( '\e812' ); 345 | background-color:#fff; 346 | color:#000; 347 | 348 | &.nav-button-left { 349 | // -- rev -- // 350 | // border-right:1px solid #ccc; 351 | } 352 | 353 | &.nav-button-right { 354 | // -- rev -- // 355 | // border-left:1px solid #ccc; 356 | } 357 | 358 | // Icon 359 | &:before{ 360 | // -- rev -- // 361 | /* 362 | top:auto; 363 | bottom:10px; 364 | left:0; 365 | color:#222; 366 | */ 367 | } 368 | 369 | @media #{$medium} { 370 | // Text 371 | &:after{ 372 | content:'Back'; 373 | } 374 | &.nav-button-left { 375 | border-right:1px solid #222; 376 | } 377 | &.nav-button-right { 378 | border-left:1px solid #222; 379 | } 380 | } 381 | 382 | &:active { 383 | color: #eee; 384 | } 385 | } 386 | .fullscreen-icon { 387 | @include icon('\e800'); 388 | position:absolute; 389 | display:block; 390 | top:80px; 391 | right:10px; 392 | width:30px; 393 | height:30px; 394 | background-color:#222; 395 | border:2px solid #fff; 396 | border-radius:1000px; 397 | z-index:1001; 398 | 399 | &:before{ 400 | color:#fff; 401 | font-size:16px; 402 | margin-top:-8px; 403 | } 404 | 405 | .contentMinimized &{ 406 | background-color:#fff; 407 | border-color:#222; 408 | @include icon('\e801'); 409 | 410 | &:before{ 411 | color:#222; 412 | font-size:16px; 413 | margin-top:-8px; 414 | } 415 | } 416 | } 417 | 418 | .scrollable{ 419 | -webkit-overflow-scrolling: touch; 420 | } 421 | 422 | %drawer{ 423 | @include transition( opacity 400ms 200ms ease ); 424 | @include box-shadow( 0 -5px 10px rgba(0,0,0,0.13) ); 425 | position:absolute; 426 | top:100%; 427 | right:0; 428 | width:100%; 429 | max-width:24rem; 430 | padding:0 15px; 431 | z-index:2; 432 | background-color:#ededed; 433 | 434 | #object &{ 435 | bottom:0; 436 | } 437 | 438 | #story &{ 439 | bottom:40px; 440 | } 441 | 442 | @media #{$small} { 443 | @include box-shadow( 5px 0 10px rgba(0,0,0,0.13) ); 444 | top:0; 445 | right:auto; 446 | left:-24rem; 447 | 448 | #story &{ 449 | top:40px; 450 | bottom:0; 451 | } 452 | } 453 | 454 | @media #{$medium} { 455 | top:0; 456 | right:auto; 457 | bottom:0; 458 | left:0; 459 | } 460 | } 461 | 462 | %handle{ 463 | // Base styles 464 | @include icon( '\e817' ); 465 | display:block; 466 | width:70px; 467 | height:70px; 468 | background-color:rgba(237,237,237,0.4); 469 | &:before{ 470 | font-size:30px; 471 | margin-top:-15px; 472 | } 473 | 474 | // Size and positioning 475 | position:fixed; 476 | margin:0; 477 | right:0; 478 | z-index:1; 479 | 480 | #object &{ 481 | bottom:0; 482 | } 483 | 484 | #story &{ 485 | bottom:40px; 486 | } 487 | 488 | @media #{$smallPortrait}{ 489 | &.attached{ 490 | position:absolute; 491 | top:0; 492 | bottom:auto; 493 | background-color:transparent; 494 | } 495 | } 496 | 497 | @media #{$small} { 498 | &:before{ 499 | @include transform( rotate( 90deg ) ); 500 | } 501 | 502 | // Size and positioning 503 | // On landscape and medium screens, button is attached regardless of class 504 | position:absolute; 505 | top:auto; 506 | right:-70px; 507 | bottom:0; 508 | left:auto; 509 | } 510 | 511 | @media #{$medium}{ 512 | display:none; 513 | } 514 | } 515 | 516 | .glance { 517 | @include transition( opacity 300ms ease ); 518 | @include transform( translate3d( 0, 0, 0 ) ); 519 | @include no-select; 520 | @include box-shadow( inset 0 -10px 10px -5px rgba(0,0,0,0.4)); 521 | background-color:rgba(255,255,255,0.65); 522 | position:absolute; 523 | display:none; 524 | width:100%; 525 | height:80px; 526 | top:-80px; 527 | left:0; 528 | margin:0; 529 | opacity:0; 530 | z-index:-1000; 531 | 532 | .drawerify-vertical.drawerify-full &{ 533 | display:block; 534 | } 535 | 536 | .drawerify-open &{ 537 | z-index:1; 538 | opacity:1; 539 | } 540 | 541 | p{ 542 | @include no-select; 543 | margin:10px; 544 | border-radius:10px; 545 | line-height:60px; 546 | vertical-align:middle; 547 | font-weight:bold; 548 | font-size:1.25em; 549 | text-align:center; 550 | } 551 | 552 | &.translucent{ 553 | opacity:0; 554 | } 555 | 556 | @media #{$small}{ 557 | display:none; 558 | } 559 | 560 | &:active { 561 | -webkit-tap-highlight-color: rgba(0,0,0,0); 562 | } 563 | 564 | .drawerify-drawer.drawerify-closed &, 565 | .drawerify-drawer.drawerify-info & { 566 | display: none; 567 | } 568 | } 569 | 570 | 571 | --------------------------------------------------------------------------------