├── .gitignore
├── 01-dbmon
├── .meteor
│ ├── .gitignore
│ ├── release
│ ├── platforms
│ ├── .finished-upgraders
│ ├── packages
│ ├── .id
│ └── versions
├── ENV.js
├── styles.css
└── client
│ ├── memory.js
│ ├── index.html
│ ├── dbmon.html
│ └── dbmon.js
├── 06-itunes-style-interface
├── .meteor
│ ├── .gitignore
│ ├── release
│ ├── platforms
│ ├── .finished-upgraders
│ ├── .id
│ ├── packages
│ └── versions
├── client
│ ├── arrow.coffee
│ ├── arrow.html
│ ├── index.coffee
│ ├── index.html
│ ├── album.html
│ ├── albums.html
│ ├── albums.coffee
│ └── album.coffee
├── public
│ ├── R-227671-1076173016.bmp.jpg
│ ├── R-2447594-1284566470.gif.jpg
│ ├── R-291038-1095516916.gif.jpg
│ ├── R-291183-1096116904.jpg.jpg
│ ├── R-749414-1329809579.jpeg.jpg
│ ├── R-1472338-1222265760.jpeg.jpg
│ ├── R-1563653-1228697990.jpeg.jpg
│ ├── R-1646356-1234303215.jpeg.jpg
│ ├── R-1830898-1246317858.jpeg.jpg
│ ├── R-1978764-1274819281.jpeg.jpg
│ ├── R-2159557-1323270394.jpeg.jpg
│ ├── R-2366230-1282923547.jpeg.jpg
│ ├── R-2709365-1297526181.jpeg.jpg
│ ├── R-119870-1353499194-8399.jpeg.jpg
│ ├── R-4471320-1365810323-8252.png.jpg
│ ├── R-673587-1360949274-6329.jpeg.jpg
│ ├── R-1226138-1393532248-7305.jpeg.jpg
│ ├── R-2045770-1360952357-4411.jpeg.jpg
│ ├── R-4548894-1368047918-8950.jpeg.jpg
│ ├── R-4928843-1379709382-2088.jpeg.jpg
│ ├── R-5251115-1388757674-4136.jpeg.jpg
│ ├── R-5283708-1389551852-3864.jpeg.jpg
│ ├── R-5354108-1391298591-9910.jpeg.jpg
│ ├── R-5527251-1395674311-9055.jpeg.jpg
│ ├── R-5555750-1396442448-5948.jpeg.jpg
│ ├── R-5669676-1419686855-3510.jpeg.jpg
│ ├── R-5685454-1399888274-8456.jpeg.jpg
│ ├── R-6037721-1409466261-6307.jpeg.jpg
│ ├── R-6347905-1417032940-3492.jpeg.jpg
│ ├── R-6386665-1417975610-5551.jpeg.jpg
│ └── R-6432244-1419282815-8594.jpeg.jpg
└── lib
│ ├── 0createStore.coffee
│ ├── Releases.coffee
│ ├── Colors.coffee
│ ├── BOB.coffee
│ └── color-thief.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.0.3.1
2 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.0.3.1
2 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/01-dbmon/ENV.js:
--------------------------------------------------------------------------------
1 | ENV = {};
2 | ENV.rows = 100;
3 | ENV.timeout = 0;
4 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/arrow.coffee:
--------------------------------------------------------------------------------
1 | Template.arrow.helpers
2 | borderBottomColor: ->
3 | Colors.get(@id)?.bg
4 |
--------------------------------------------------------------------------------
/01-dbmon/styles.css:
--------------------------------------------------------------------------------
1 | .Query {
2 | position: relative;
3 | }
4 |
5 | .Query:hover .popover {
6 | left: -100%;
7 | width: 100%;
8 | display: block;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-227671-1076173016.bmp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-227671-1076173016.bmp.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-2447594-1284566470.gif.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-2447594-1284566470.gif.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-291038-1095516916.gif.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-291038-1095516916.gif.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-291183-1096116904.jpg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-291183-1096116904.jpg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-749414-1329809579.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-749414-1329809579.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1472338-1222265760.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1472338-1222265760.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1563653-1228697990.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1563653-1228697990.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1646356-1234303215.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1646356-1234303215.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1830898-1246317858.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1830898-1246317858.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1978764-1274819281.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1978764-1274819281.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-2159557-1323270394.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-2159557-1323270394.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-2366230-1282923547.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-2366230-1282923547.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-2709365-1297526181.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-2709365-1297526181.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-119870-1353499194-8399.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-119870-1353499194-8399.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-4471320-1365810323-8252.png.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-4471320-1365810323-8252.png.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-673587-1360949274-6329.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-673587-1360949274-6329.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-1226138-1393532248-7305.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-1226138-1393532248-7305.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-2045770-1360952357-4411.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-2045770-1360952357-4411.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-4548894-1368047918-8950.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-4548894-1368047918-8950.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-4928843-1379709382-2088.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-4928843-1379709382-2088.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5251115-1388757674-4136.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5251115-1388757674-4136.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5283708-1389551852-3864.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5283708-1389551852-3864.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5354108-1391298591-9910.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5354108-1391298591-9910.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5527251-1395674311-9055.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5527251-1395674311-9055.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5555750-1396442448-5948.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5555750-1396442448-5948.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5669676-1419686855-3510.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5669676-1419686855-3510.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-5685454-1399888274-8456.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-5685454-1399888274-8456.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-6037721-1409466261-6307.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-6037721-1409466261-6307.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-6347905-1417032940-3492.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-6347905-1417032940-3492.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-6386665-1417975610-5551.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-6386665-1417975610-5551.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/public/R-6432244-1419282815-8594.jpeg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitar/reactconf-2015-HYPE/HEAD/06-itunes-style-interface/public/R-6432244-1419282815-8594.jpeg.jpg
--------------------------------------------------------------------------------
/06-itunes-style-interface/lib/0createStore.coffee:
--------------------------------------------------------------------------------
1 | @createStore = (loadFn) ->
2 | cache = {}
3 |
4 | get: (id) ->
5 | return null unless id
6 |
7 | cache[id] = loadFn id unless cache[id]
8 | cache[id].get()
9 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/lib/Releases.coffee:
--------------------------------------------------------------------------------
1 | API = 'https://api.discogs.com/releases'
2 |
3 | @Releases = createStore (id) ->
4 | result = new ReactiveVar null
5 |
6 | HTTP.get "#{API}/#{id}", (error, response) ->
7 | result.set response.data
8 |
9 | result
10 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/arrow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-platform
8 | autopublish
9 | insecure
10 | reactive-var
11 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | pua4m4wbqq6g1pk94dr
8 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | wqea8w12zozl4eavi87
8 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/index.coffee:
--------------------------------------------------------------------------------
1 | Router.route '/',
2 | name: 'index'
3 | template: 'index'
4 |
5 | Router.route '/album/:id',
6 | name: 'album'
7 | template: 'albums'
8 |
9 | action: ->
10 | Tracker.nonreactive =>
11 | @state.set 'oldId', @state.get 'id'
12 | @state.set 'id', @params.id if Releases.get(@params.id) and Colors.get(@params.id)
13 | @render()
14 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-platform
8 | autopublish
9 | insecure
10 | coffeescript
11 | reactive-var
12 | http
13 | iron:router
14 |
--------------------------------------------------------------------------------
/01-dbmon/client/memory.js:
--------------------------------------------------------------------------------
1 | Meteor.startup(function () {
2 | var stats = new MemoryStats();
3 | stats.domElement.style.position = 'fixed';
4 | stats.domElement.style.right = '0px';
5 | stats.domElement.style.bottom = '0px';
6 | document.body.appendChild( stats.domElement );
7 | requestAnimationFrame(function rAFloop(){
8 | stats.update();
9 | requestAnimationFrame(rAFloop);
10 | })
11 | });
12 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | HYPE!
4 |
9 |
10 |
11 |
12 | {{> albums}}
13 |
14 |
--------------------------------------------------------------------------------
/01-dbmon/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | dbmon (Blaze)
6 |
7 |
8 |
9 |
10 |
11 | {{> dbmon}}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | HYPE!
2 | ====
3 |
4 | Meteor versions of demo programs from [Hype! React Conf talk from 2015](https://www.youtube.com/watch?v=z5e7kWSHWTg)
5 | ([original source code](https://github.com/ryanflorence/reactconf-2015-HYPE)).
6 |
7 | * `01-dbmon` – http://dbmon.meteor.com/
8 | * `06-itunes-style-interface` – http://itunes.meteor.com/
9 |
10 | Running
11 | -------
12 |
13 | ```sh
14 | curl https://install.meteor.com/ | sh
15 | cd
16 | meteor
17 | open http://localhost:3000
18 | ```
19 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/album.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 | {{#each tracklist}}
14 | {{title}} ({{duration}})
15 | {{/each}}
16 |
17 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/lib/Colors.coffee:
--------------------------------------------------------------------------------
1 | dark = (r, g, b) ->
2 | # YIQ equation from http://24ways.org/2010/calculating-color-contrast
3 | yiq = (r * 299 + g * 587 + b * 114) / 1000
4 | yiq < 128
5 |
6 | light = (r, g, b) ->
7 | not dark r, g, b
8 |
9 | @Colors = createStore (id) ->
10 | result = new ReactiveVar null
11 |
12 | img = new Image()
13 | img.onload = ->
14 | thief = new ColorThief()
15 | [r, g, b] = thief.getColor img
16 | bg = "rgb(#{r}, #{g}, #{b})"
17 | fg = if light r, g, b then '#000' else '#fff'
18 | result.set {bg, fg}
19 | img.src = BOB[id]
20 |
21 | result
22 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/albums.html:
--------------------------------------------------------------------------------
1 |
2 | {{> releases}}
3 |
4 |
5 |
6 | {{#each rows}}
7 | {{> row}}
8 | {{/each}}
9 |
10 |
11 |
12 | {{#each this}}{{> column}}{{/each}}{{> release}}
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 | {{#if isCurrent}}{{> arrow}}{{/if}}
24 |
25 |
26 |
27 |
28 | {{#if currentRelease}}
29 | {{> album currentRelease}}
30 | {{else}}
31 | {{#if previousRelease}}
32 | {{> album previousRelease}}
33 | {{/if}}
34 | {{/if}}
35 |
36 |
--------------------------------------------------------------------------------
/01-dbmon/client/dbmon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if elapsed}}{{formatElapsed elapsed}}{{/if}}
4 |
8 | |
9 |
10 |
11 |
12 |
13 |
14 | {{queries.length}}
15 |
16 | |
17 | {{#each topFiveQueries}}
18 | {{> query}}
19 | {{/each}}
20 |
21 |
22 |
23 |
24 | |
25 | {{dbname}}
26 | |
27 | {{> sample lastSample}}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {{#each databases}}
36 | {{> database}}
37 | {{/each}}
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/01-dbmon/.meteor/versions:
--------------------------------------------------------------------------------
1 | application-configuration@1.0.4
2 | autopublish@1.0.2
3 | autoupdate@1.1.5
4 | base64@1.0.2
5 | binary-heap@1.0.2
6 | blaze@2.0.4
7 | blaze-tools@1.0.2
8 | boilerplate-generator@1.0.2
9 | callback-hook@1.0.2
10 | check@1.0.4
11 | ddp@1.0.14
12 | deps@1.0.6
13 | ejson@1.0.5
14 | fastclick@1.0.2
15 | follower-livedata@1.0.3
16 | geojson-utils@1.0.2
17 | html-tools@1.0.3
18 | htmljs@1.0.3
19 | http@1.0.10
20 | id-map@1.0.2
21 | insecure@1.0.2
22 | jquery@1.11.3
23 | json@1.0.2
24 | launch-screen@1.0.1
25 | livedata@1.0.12
26 | logging@1.0.6
27 | meteor@1.1.4
28 | meteor-platform@1.2.1
29 | minifiers@1.1.3
30 | minimongo@1.0.6
31 | mobile-status-bar@1.0.2
32 | mongo@1.0.11
33 | observe-sequence@1.0.4
34 | ordered-dict@1.0.2
35 | random@1.0.2
36 | reactive-dict@1.0.5
37 | reactive-var@1.0.4
38 | reload@1.1.2
39 | retry@1.0.2
40 | routepolicy@1.0.4
41 | session@1.0.5
42 | spacebars@1.0.5
43 | spacebars-compiler@1.0.4
44 | templating@1.0.11
45 | tracker@1.0.5
46 | ui@1.0.5
47 | underscore@1.0.2
48 | url@1.0.3
49 | webapp@1.1.6
50 | webapp-hashing@1.0.2
51 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/albums.coffee:
--------------------------------------------------------------------------------
1 | IMAGE_SIZE = 100
2 | IMAGE_MARGIN = 10
3 |
4 | viewportWidth = new ReactiveVar $(window).width()
5 |
6 | Meteor.startup ->
7 | $(window).resize (event) ->
8 | viewportWidth.set $(window).width()
9 | return
10 |
11 | calcAlbumsPerRow = ->
12 | fullWidth = IMAGE_SIZE + (IMAGE_MARGIN * 2)
13 | Math.floor(viewportWidth.get() / fullWidth)
14 |
15 | Template.releases.helpers
16 | rows: ->
17 | albumsPerRow = calcAlbumsPerRow()
18 |
19 | Object.keys(BOB).reduce (rows, key, index) ->
20 | rows.push [] if index % albumsPerRow is 0
21 | rows[rows.length - 1].push id: key, file: BOB[key]
22 | rows
23 | ,
24 | []
25 |
26 | Template.column.helpers
27 | IMAGE_MARGIN: ->
28 | IMAGE_MARGIN
29 |
30 | IMAGE_SIZE: ->
31 | IMAGE_SIZE
32 |
33 | isCurrent: ->
34 | Iron.controller().state.get('id') is @id
35 |
36 | release = (data, fieldName) ->
37 | currentAlbum = _.findWhere data, id: Iron.controller().state.get(fieldName)
38 | return unless currentAlbum
39 | Releases.get currentAlbum.id
40 |
41 | Template.release.helpers
42 | currentRelease: ->
43 | release @, 'id'
44 |
45 | previousRelease: ->
46 | release @, 'oldId'
47 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/.meteor/versions:
--------------------------------------------------------------------------------
1 | application-configuration@1.0.4
2 | autopublish@1.0.2
3 | autoupdate@1.1.5
4 | base64@1.0.2
5 | binary-heap@1.0.2
6 | blaze@2.0.4
7 | blaze-tools@1.0.2
8 | boilerplate-generator@1.0.2
9 | callback-hook@1.0.2
10 | check@1.0.4
11 | coffeescript@1.0.5
12 | ddp@1.0.14
13 | deps@1.0.6
14 | ejson@1.0.5
15 | fastclick@1.0.2
16 | follower-livedata@1.0.3
17 | geojson-utils@1.0.2
18 | html-tools@1.0.3
19 | htmljs@1.0.3
20 | http@1.0.10
21 | id-map@1.0.2
22 | insecure@1.0.2
23 | iron:controller@1.0.7
24 | iron:core@1.0.7
25 | iron:dynamic-template@1.0.7
26 | iron:layout@1.0.7
27 | iron:location@1.0.7
28 | iron:middleware-stack@1.0.7
29 | iron:router@1.0.7
30 | iron:url@1.0.7
31 | jquery@1.11.3
32 | json@1.0.2
33 | launch-screen@1.0.1
34 | livedata@1.0.12
35 | logging@1.0.6
36 | meteor@1.1.4
37 | meteor-platform@1.2.1
38 | minifiers@1.1.3
39 | minimongo@1.0.6
40 | mobile-status-bar@1.0.2
41 | mongo@1.0.11
42 | observe-sequence@1.0.4
43 | ordered-dict@1.0.2
44 | random@1.0.2
45 | reactive-dict@1.0.5
46 | reactive-var@1.0.4
47 | reload@1.1.2
48 | retry@1.0.2
49 | routepolicy@1.0.4
50 | session@1.0.5
51 | spacebars@1.0.5
52 | spacebars-compiler@1.0.4
53 | templating@1.0.11
54 | tracker@1.0.5
55 | ui@1.0.5
56 | underscore@1.0.2
57 | url@1.0.3
58 | webapp@1.1.6
59 | webapp-hashing@1.0.2
60 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/client/album.coffee:
--------------------------------------------------------------------------------
1 | isOld = (data) ->
2 | oldId = Iron.controller().state.get('oldId')
3 | # Release API result id is a number, so we have to stringify
4 | # it because we are using string IDs everywhere else.
5 | oldId is "#{ data.id }"
6 |
7 | Template.album.created = ->
8 | if isOld @data
9 | # Set the height of the tracklist initially to the "auto"
10 | # so that we can animate it shrinking.
11 | @_height = new ReactiveVar 'auto'
12 | else
13 | # Set the height of the tracklist initially to the "0"
14 | # so that we can animate it growing.
15 | @_height = new ReactiveVar '0px'
16 |
17 | Template.album.rendered = ->
18 | if isOld @data
19 | # After the tracklist has been added to the DOM, set fixed height.
20 | @_height.set "#{ @$('.container').outerHeight() }px"
21 |
22 | # And then shrink it.
23 | Meteor.defer =>
24 | @_height.set '0px'
25 |
26 | else
27 | @autorun (computation) =>
28 | # Registers a data context dependency.
29 | Template.currentData()
30 | Tracker.afterFlush =>
31 | # After the tracklist has been updated in the DOM,
32 | # resize it to the new height with animation.
33 | @_height.set "#{ @$('.container').outerHeight() }px"
34 |
35 | Template.album.helpers
36 | fg: ->
37 | Colors.get(@id)?.fg
38 |
39 | bg: ->
40 | Colors.get(@id)?.bg
41 |
42 | height: ->
43 | Template.instance()._height.get()
44 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/lib/BOB.coffee:
--------------------------------------------------------------------------------
1 | @BOB =
2 | 119870: '/R-119870-1353499194-8399.jpeg.jpg'
3 | 227671: '/R-227671-1076173016.bmp.jpg'
4 | 291038: '/R-291038-1095516916.gif.jpg'
5 | 291183: '/R-291183-1096116904.jpg.jpg'
6 | 673587: '/R-673587-1360949274-6329.jpeg.jpg'
7 | 749414: '/R-749414-1329809579.jpeg.jpg'
8 | 1226138: '/R-1226138-1393532248-7305.jpeg.jpg'
9 | 1472338: '/R-1472338-1222265760.jpeg.jpg'
10 | 1563653: '/R-1563653-1228697990.jpeg.jpg'
11 | 1646356: '/R-1646356-1234303215.jpeg.jpg'
12 | 1830898: '/R-1830898-1246317858.jpeg.jpg'
13 | 1978764: '/R-1978764-1274819281.jpeg.jpg'
14 | 2045770: '/R-2045770-1360952357-4411.jpeg.jpg'
15 | 2159557: '/R-2159557-1323270394.jpeg.jpg'
16 | 2366230: '/R-2366230-1282923547.jpeg.jpg'
17 | 2447594: '/R-2447594-1284566470.gif.jpg'
18 | 2709365: '/R-2709365-1297526181.jpeg.jpg'
19 | 4471320: '/R-4471320-1365810323-8252.png.jpg'
20 | 4548894: '/R-4548894-1368047918-8950.jpeg.jpg'
21 | 4928843: '/R-4928843-1379709382-2088.jpeg.jpg'
22 | 5251115: '/R-5251115-1388757674-4136.jpeg.jpg'
23 | 5283708: '/R-5283708-1389551852-3864.jpeg.jpg'
24 | 5354108: '/R-5354108-1391298591-9910.jpeg.jpg'
25 | 5527251: '/R-5527251-1395674311-9055.jpeg.jpg'
26 | 5555750: '/R-5555750-1396442448-5948.jpeg.jpg'
27 | 5669676: '/R-5669676-1419686855-3510.jpeg.jpg'
28 | 5685454: '/R-5685454-1399888274-8456.jpeg.jpg'
29 | 6037721: '/R-6037721-1409466261-6307.jpeg.jpg'
30 | 6347905: '/R-6347905-1417032940-3492.jpeg.jpg'
31 | 6386665: '/R-6386665-1417975610-5551.jpeg.jpg'
32 | 6432244: '/R-6432244-1419282815-8594.jpeg.jpg'
33 |
--------------------------------------------------------------------------------
/01-dbmon/client/dbmon.js:
--------------------------------------------------------------------------------
1 | var start = Date.now();
2 | var loadCount = 0;
3 |
4 | function getData() {
5 | // generate some dummy data
6 | var data = {
7 | start_at: new Date().getTime() / 1000,
8 | databases: {}
9 | };
10 |
11 | for (var i = 1; i <= ENV.rows; i++) {
12 | data.databases["cluster" + i] = {
13 | queries: []
14 | };
15 |
16 | data.databases["cluster" + i + "slave"] = {
17 | queries: []
18 | };
19 | }
20 |
21 | Object.keys(data.databases).forEach(function(dbname) {
22 | var info = data.databases[dbname];
23 |
24 | var r = Math.floor((Math.random() * 10) + 1);
25 | for (var i = 0; i < r; i++) {
26 | var q = {
27 | canvas_action: null,
28 | canvas_context_id: null,
29 | canvas_controller: null,
30 | canvas_hostname: null,
31 | canvas_job_tag: null,
32 | canvas_pid: null,
33 | elapsed: Math.random() * 15,
34 | query: "SELECT blah FROM something",
35 | waiting: Math.random() < 0.5
36 | };
37 |
38 | if (Math.random() < 0.2) {
39 | q.query = " in transaction";
40 | }
41 |
42 | if (Math.random() < 0.1) {
43 | q.query = "vacuum";
44 | }
45 |
46 | info.queries.push(q);
47 | }
48 |
49 | info.queries = info.queries.sort(function(a, b) {
50 | return b.elapsed - a.elapsed;
51 | });
52 | });
53 |
54 | return data;
55 | }
56 |
57 | var _base;
58 |
59 | (_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
60 | return padding.repeat((toLength - this.length) / padding.length).concat(this);
61 | });
62 |
63 | Template.query.helpers({
64 | className: function () {
65 | var className = "elapsed short";
66 | if (this.elapsed >= 10.0) {
67 | className = "elapsed warn_long";
68 | }
69 | else if (this.elapsed >= 1.0) {
70 | className = "elapsed warn";
71 | }
72 | return className;
73 | },
74 |
75 | formatElapsed: function (value) {
76 | var str = parseFloat(value).toFixed(2);
77 | if (value > 60) {
78 | var minutes = Math.floor(value / 60);
79 | var comps = (value % 60).toFixed(2).split('.');
80 | var seconds = comps[0].lpad('0', 2);
81 | var ms = comps[1];
82 | str = minutes + ":" + seconds + "." + ms;
83 | }
84 | return str;
85 | }
86 | });
87 |
88 | Template.sample.helpers({
89 | countClassName: function () {
90 | var countClassName = "label";
91 | if (this.queries.length >= 20) {
92 | countClassName += " label-important";
93 | }
94 | else if (this.queries.length >= 10) {
95 | countClassName += " label-warning";
96 | }
97 | else {
98 | countClassName += " label-success";
99 | }
100 | return countClassName;
101 | },
102 |
103 | topFiveQueries: function () {
104 | var topFiveQueries = this.queries.slice(0, 5);
105 | while (topFiveQueries.length < 5) {
106 | topFiveQueries.push({ query: "" });
107 | }
108 | return topFiveQueries;
109 | }
110 | });
111 |
112 | Template.database.helpers({
113 | lastSample: function () {
114 | return this.samples[this.samples.length - 1];
115 | }
116 | });
117 |
118 | Template.dbmon.created = function () {
119 | this._databases = new ReactiveVar({});
120 |
121 | this._loadSamples = function () {
122 | var databases = this._databases.get();
123 |
124 | loadCount++;
125 | var newData = getData();
126 |
127 | Object.keys(newData.databases).forEach(function(dbname) {
128 | var sampleInfo = newData.databases[dbname];
129 |
130 | if (!databases[dbname]) {
131 | databases[dbname] = {
132 | name: dbname,
133 | samples: []
134 | }
135 | }
136 |
137 | var samples = databases[dbname].samples;
138 | samples.push({
139 | time: newData.start_at,
140 | queries: sampleInfo.queries
141 | });
142 | if (samples.length > 5) {
143 | samples.splice(0, samples.length - 5);
144 | }
145 | }.bind(this));
146 |
147 | this._databases.set(databases);
148 |
149 | Meteor.setTimeout(this._loadSamples, ENV.timeout);
150 | }.bind(this);
151 | };
152 |
153 | Template.dbmon.rendered = function () {
154 | this._loadSamples();
155 | };
156 |
157 | Template.dbmon.helpers({
158 | databases: function () {
159 | var databasesArray = [];
160 | var databases = Template.instance()._databases.get();
161 | Object.keys(databases).forEach(function(dbname) {
162 | databasesArray.push({
163 | _id: dbname,
164 | dbname: dbname,
165 | samples: databases[dbname].samples
166 | });
167 | });
168 | return databasesArray;
169 | }
170 | });
171 |
--------------------------------------------------------------------------------
/06-itunes-style-interface/lib/color-thief.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Color Thief v2.0
3 | * by Lokesh Dhakar - http://www.lokeshdhakar.com
4 | *
5 | * License
6 | * -------
7 | * Creative Commons Attribution 2.5 License:
8 | * http://creativecommons.org/licenses/by/2.5/
9 | *
10 | * Thanks
11 | * ------
12 | * Nick Rabinowitz - For creating quantize.js.
13 | * John Schulz - For clean up and optimization. @JFSIII
14 | * Nathan Spady - For adding drag and drop support to the demo page.
15 | *
16 | */
17 |
18 | /*
19 | CanvasImage Class
20 | Class that wraps the html image element and canvas.
21 | It also simplifies some of the canvas context manipulation
22 | with a set of helper functions.
23 | */
24 | var CanvasImage = function (image) {
25 | this.canvas = document.createElement('canvas');
26 | this.context = this.canvas.getContext('2d');
27 |
28 | document.body.appendChild(this.canvas);
29 |
30 | this.width = this.canvas.width = image.width;
31 | this.height = this.canvas.height = image.height;
32 |
33 | this.context.drawImage(image, 0, 0, this.width, this.height);
34 | };
35 |
36 | CanvasImage.prototype.clear = function () {
37 | this.context.clearRect(0, 0, this.width, this.height);
38 | };
39 |
40 | CanvasImage.prototype.update = function (imageData) {
41 | this.context.putImageData(imageData, 0, 0);
42 | };
43 |
44 | CanvasImage.prototype.getPixelCount = function () {
45 | return this.width * this.height;
46 | };
47 |
48 | CanvasImage.prototype.getImageData = function () {
49 | return this.context.getImageData(0, 0, this.width, this.height);
50 | };
51 |
52 | CanvasImage.prototype.removeCanvas = function () {
53 | this.canvas.parentNode.removeChild(this.canvas);
54 | };
55 |
56 |
57 | ColorThief = function () {};
58 |
59 | /*
60 | * getColor(sourceImage[, quality])
61 | * returns {r: num, g: num, b: num}
62 | *
63 | * Use the median cut algorithm provided by quantize.js to cluster similar
64 | * colors and return the base color from the largest cluster.
65 | *
66 | * Quality is an optional argument. It needs to be an integer. 0 is the highest quality settings.
67 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
68 | * faster a color will be returned but the greater the likelihood that it will not be the visually
69 | * most dominant color.
70 | *
71 | * */
72 | ColorThief.prototype.getColor = function(sourceImage, quality) {
73 | var palette = this.getPalette(sourceImage, 5, quality);
74 | var dominantColor = palette[0];
75 | return dominantColor;
76 | };
77 |
78 |
79 | /*
80 | * getPalette(sourceImage[, colorCount, quality])
81 | * returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
82 | *
83 | * Use the median cut algorithm provided by quantize.js to cluster similar colors.
84 | *
85 | * colorCount determines the size of the palette; the number of colors returned. If not set, it
86 | * defaults to 10.
87 | *
88 | * BUGGY: Function does not always return the requested amount of colors. It can be +/- 2.
89 | *
90 | * quality is an optional argument. It needs to be an integer. 0 is the highest quality settings.
91 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
92 | * faster the palette generation but the greater the likelihood that colors will be missed.
93 | *
94 | *
95 | */
96 | ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) {
97 |
98 | if (typeof colorCount === 'undefined') {
99 | colorCount = 10;
100 | }
101 | if (typeof quality === 'undefined') {
102 | quality = 10;
103 | }
104 |
105 | // Create custom CanvasImage object
106 | var image = new CanvasImage(sourceImage);
107 | var imageData = image.getImageData();
108 | var pixels = imageData.data;
109 | var pixelCount = image.getPixelCount();
110 |
111 | // Store the RGB values in an array format suitable for quantize function
112 | var pixelArray = [];
113 | for (var i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
114 | offset = i * 4;
115 | r = pixels[offset + 0];
116 | g = pixels[offset + 1];
117 | b = pixels[offset + 2];
118 | a = pixels[offset + 3];
119 | // If pixel is mostly opaque and not white
120 | if (a >= 125) {
121 | if (!(r > 250 && g > 250 && b > 250)) {
122 | pixelArray.push([r, g, b]);
123 | }
124 | }
125 | }
126 |
127 | // Send array to quantize function which clusters values
128 | // using median cut algorithm
129 | var cmap = MMCQ.quantize(pixelArray, colorCount);
130 | var palette = cmap.palette();
131 |
132 | // Clean up
133 | image.removeCanvas();
134 |
135 | return palette;
136 | };
137 |
138 |
139 |
140 |
141 | /*!
142 | * quantize.js Copyright 2008 Nick Rabinowitz.
143 | * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
144 | */
145 |
146 | // fill out a couple protovis dependencies
147 | /*!
148 | * Block below copied from Protovis: http://mbostock.github.com/protovis/
149 | * Copyright 2010 Stanford Visualization Group
150 | * Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
151 | */
152 | if (!pv) {
153 | var pv = {
154 | map: function(array, f) {
155 | var o = {};
156 | return f ? array.map(function(d, i) { o.index = i; return f.call(o, d); }) : array.slice();
157 | },
158 | naturalOrder: function(a, b) {
159 | return (a < b) ? -1 : ((a > b) ? 1 : 0);
160 | },
161 | sum: function(array, f) {
162 | var o = {};
163 | return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0);
164 | },
165 | max: function(array, f) {
166 | return Math.max.apply(null, f ? pv.map(array, f) : array);
167 | }
168 | };
169 | }
170 |
171 |
172 |
173 | /**
174 | * Basic Javascript port of the MMCQ (modified median cut quantization)
175 | * algorithm from the Leptonica library (http://www.leptonica.com/).
176 | * Returns a color map you can use to map original pixels to the reduced
177 | * palette. Still a work in progress.
178 | *
179 | * @author Nick Rabinowitz
180 | * @example
181 |
182 | // array of pixels as [R,G,B] arrays
183 | var myPixels = [[190,197,190], [202,204,200], [207,214,210], [211,214,211], [205,207,207]
184 | // etc
185 | ];
186 | var maxColors = 4;
187 |
188 | var cmap = MMCQ.quantize(myPixels, maxColors);
189 | var newPalette = cmap.palette();
190 | var newPixels = myPixels.map(function(p) {
191 | return cmap.map(p);
192 | });
193 |
194 | */
195 | var MMCQ = (function() {
196 | // private constants
197 | var sigbits = 5,
198 | rshift = 8 - sigbits,
199 | maxIterations = 1000,
200 | fractByPopulations = 0.75;
201 |
202 | // get reduced-space color index for a pixel
203 | function getColorIndex(r, g, b) {
204 | return (r << (2 * sigbits)) + (g << sigbits) + b;
205 | }
206 |
207 | // Simple priority queue
208 | function PQueue(comparator) {
209 | var contents = [],
210 | sorted = false;
211 |
212 | function sort() {
213 | contents.sort(comparator);
214 | sorted = true;
215 | }
216 |
217 | return {
218 | push: function(o) {
219 | contents.push(o);
220 | sorted = false;
221 | },
222 | peek: function(index) {
223 | if (!sorted) sort();
224 | if (index===undefined) index = contents.length - 1;
225 | return contents[index];
226 | },
227 | pop: function() {
228 | if (!sorted) sort();
229 | return contents.pop();
230 | },
231 | size: function() {
232 | return contents.length;
233 | },
234 | map: function(f) {
235 | return contents.map(f);
236 | },
237 | debug: function() {
238 | if (!sorted) sort();
239 | return contents;
240 | }
241 | };
242 | }
243 |
244 | // 3d color space box
245 | function VBox(r1, r2, g1, g2, b1, b2, histo) {
246 | var vbox = this;
247 | vbox.r1 = r1;
248 | vbox.r2 = r2;
249 | vbox.g1 = g1;
250 | vbox.g2 = g2;
251 | vbox.b1 = b1;
252 | vbox.b2 = b2;
253 | vbox.histo = histo;
254 | }
255 | VBox.prototype = {
256 | volume: function(force) {
257 | var vbox = this;
258 | if (!vbox._volume || force) {
259 | vbox._volume = ((vbox.r2 - vbox.r1 + 1) * (vbox.g2 - vbox.g1 + 1) * (vbox.b2 - vbox.b1 + 1));
260 | }
261 | return vbox._volume;
262 | },
263 | count: function(force) {
264 | var vbox = this,
265 | histo = vbox.histo;
266 | if (!vbox._count_set || force) {
267 | var npix = 0,
268 | i, j, k;
269 | for (i = vbox.r1; i <= vbox.r2; i++) {
270 | for (j = vbox.g1; j <= vbox.g2; j++) {
271 | for (k = vbox.b1; k <= vbox.b2; k++) {
272 | index = getColorIndex(i,j,k);
273 | npix += (histo[index] || 0);
274 | }
275 | }
276 | }
277 | vbox._count = npix;
278 | vbox._count_set = true;
279 | }
280 | return vbox._count;
281 | },
282 | copy: function() {
283 | var vbox = this;
284 | return new VBox(vbox.r1, vbox.r2, vbox.g1, vbox.g2, vbox.b1, vbox.b2, vbox.histo);
285 | },
286 | avg: function(force) {
287 | var vbox = this,
288 | histo = vbox.histo;
289 | if (!vbox._avg || force) {
290 | var ntot = 0,
291 | mult = 1 << (8 - sigbits),
292 | rsum = 0,
293 | gsum = 0,
294 | bsum = 0,
295 | hval,
296 | i, j, k, histoindex;
297 | for (i = vbox.r1; i <= vbox.r2; i++) {
298 | for (j = vbox.g1; j <= vbox.g2; j++) {
299 | for (k = vbox.b1; k <= vbox.b2; k++) {
300 | histoindex = getColorIndex(i,j,k);
301 | hval = histo[histoindex] || 0;
302 | ntot += hval;
303 | rsum += (hval * (i + 0.5) * mult);
304 | gsum += (hval * (j + 0.5) * mult);
305 | bsum += (hval * (k + 0.5) * mult);
306 | }
307 | }
308 | }
309 | if (ntot) {
310 | vbox._avg = [~~(rsum/ntot), ~~(gsum/ntot), ~~(bsum/ntot)];
311 | } else {
312 | // console.log('empty box');
313 | vbox._avg = [
314 | ~~(mult * (vbox.r1 + vbox.r2 + 1) / 2),
315 | ~~(mult * (vbox.g1 + vbox.g2 + 1) / 2),
316 | ~~(mult * (vbox.b1 + vbox.b2 + 1) / 2)
317 | ];
318 | }
319 | }
320 | return vbox._avg;
321 | },
322 | contains: function(pixel) {
323 | var vbox = this,
324 | rval = pixel[0] >> rshift;
325 | gval = pixel[1] >> rshift;
326 | bval = pixel[2] >> rshift;
327 | return (rval >= vbox.r1 && rval <= vbox.r2 &&
328 | gval >= vbox.g1 && gval <= vbox.g2 &&
329 | bval >= vbox.b1 && bval <= vbox.b2);
330 | }
331 | };
332 |
333 | // Color map
334 | function CMap() {
335 | this.vboxes = new PQueue(function(a,b) {
336 | return pv.naturalOrder(
337 | a.vbox.count()*a.vbox.volume(),
338 | b.vbox.count()*b.vbox.volume()
339 | );
340 | });
341 | }
342 | CMap.prototype = {
343 | push: function(vbox) {
344 | this.vboxes.push({
345 | vbox: vbox,
346 | color: vbox.avg()
347 | });
348 | },
349 | palette: function() {
350 | return this.vboxes.map(function(vb) { return vb.color; });
351 | },
352 | size: function() {
353 | return this.vboxes.size();
354 | },
355 | map: function(color) {
356 | var vboxes = this.vboxes;
357 | for (var i=0; i 251
391 | var idx = vboxes.length-1,
392 | highest = vboxes[idx].color;
393 | if (highest[0] > 251 && highest[1] > 251 && highest[2] > 251)
394 | vboxes[idx].color = [255,255,255];
395 | }
396 | };
397 |
398 | // histo (1-d array, giving the number of pixels in
399 | // each quantized region of color space), or null on error
400 | function getHisto(pixels) {
401 | var histosize = 1 << (3 * sigbits),
402 | histo = new Array(histosize),
403 | index, rval, gval, bval;
404 | pixels.forEach(function(pixel) {
405 | rval = pixel[0] >> rshift;
406 | gval = pixel[1] >> rshift;
407 | bval = pixel[2] >> rshift;
408 | index = getColorIndex(rval, gval, bval);
409 | histo[index] = (histo[index] || 0) + 1;
410 | });
411 | return histo;
412 | }
413 |
414 | function vboxFromPixels(pixels, histo) {
415 | var rmin=1000000, rmax=0,
416 | gmin=1000000, gmax=0,
417 | bmin=1000000, bmax=0,
418 | rval, gval, bval;
419 | // find min/max
420 | pixels.forEach(function(pixel) {
421 | rval = pixel[0] >> rshift;
422 | gval = pixel[1] >> rshift;
423 | bval = pixel[2] >> rshift;
424 | if (rval < rmin) rmin = rval;
425 | else if (rval > rmax) rmax = rval;
426 | if (gval < gmin) gmin = gval;
427 | else if (gval > gmax) gmax = gval;
428 | if (bval < bmin) bmin = bval;
429 | else if (bval > bmax) bmax = bval;
430 | });
431 | return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
432 | }
433 |
434 | function medianCutApply(histo, vbox) {
435 | if (!vbox.count()) return;
436 |
437 | var rw = vbox.r2 - vbox.r1 + 1,
438 | gw = vbox.g2 - vbox.g1 + 1,
439 | bw = vbox.b2 - vbox.b1 + 1,
440 | maxw = pv.max([rw, gw, bw]);
441 | // only one pixel, no split
442 | if (vbox.count() == 1) {
443 | return [vbox.copy()];
444 | }
445 | /* Find the partial sum arrays along the selected axis. */
446 | var total = 0,
447 | partialsum = [],
448 | lookaheadsum = [],
449 | i, j, k, sum, index;
450 | if (maxw == rw) {
451 | for (i = vbox.r1; i <= vbox.r2; i++) {
452 | sum = 0;
453 | for (j = vbox.g1; j <= vbox.g2; j++) {
454 | for (k = vbox.b1; k <= vbox.b2; k++) {
455 | index = getColorIndex(i,j,k);
456 | sum += (histo[index] || 0);
457 | }
458 | }
459 | total += sum;
460 | partialsum[i] = total;
461 | }
462 | }
463 | else if (maxw == gw) {
464 | for (i = vbox.g1; i <= vbox.g2; i++) {
465 | sum = 0;
466 | for (j = vbox.r1; j <= vbox.r2; j++) {
467 | for (k = vbox.b1; k <= vbox.b2; k++) {
468 | index = getColorIndex(j,i,k);
469 | sum += (histo[index] || 0);
470 | }
471 | }
472 | total += sum;
473 | partialsum[i] = total;
474 | }
475 | }
476 | else { /* maxw == bw */
477 | for (i = vbox.b1; i <= vbox.b2; i++) {
478 | sum = 0;
479 | for (j = vbox.r1; j <= vbox.r2; j++) {
480 | for (k = vbox.g1; k <= vbox.g2; k++) {
481 | index = getColorIndex(j,k,i);
482 | sum += (histo[index] || 0);
483 | }
484 | }
485 | total += sum;
486 | partialsum[i] = total;
487 | }
488 | }
489 | partialsum.forEach(function(d,i) {
490 | lookaheadsum[i] = total-d;
491 | });
492 | function doCut(color) {
493 | var dim1 = color + '1',
494 | dim2 = color + '2',
495 | left, right, vbox1, vbox2, d2, count2=0;
496 | for (i = vbox[dim1]; i <= vbox[dim2]; i++) {
497 | if (partialsum[i] > total / 2) {
498 | vbox1 = vbox.copy();
499 | vbox2 = vbox.copy();
500 | left = i - vbox[dim1];
501 | right = vbox[dim2] - i;
502 | if (left <= right)
503 | d2 = Math.min(vbox[dim2] - 1, ~~(i + right / 2));
504 | else d2 = Math.max(vbox[dim1], ~~(i - 1 - left / 2));
505 | // avoid 0-count boxes
506 | while (!partialsum[d2]) d2++;
507 | count2 = lookaheadsum[d2];
508 | while (!count2 && partialsum[d2-1]) count2 = lookaheadsum[--d2];
509 | // set dimensions
510 | vbox1[dim2] = d2;
511 | vbox2[dim1] = vbox1[dim2] + 1;
512 | // console.log('vbox counts:', vbox.count(), vbox1.count(), vbox2.count());
513 | return [vbox1, vbox2];
514 | }
515 | }
516 |
517 | }
518 | // determine the cut planes
519 | return maxw == rw ? doCut('r') :
520 | maxw == gw ? doCut('g') :
521 | doCut('b');
522 | }
523 |
524 | function quantize(pixels, maxcolors) {
525 | // short-circuit
526 | if (!pixels.length || maxcolors < 2 || maxcolors > 256) {
527 | // console.log('wrong number of maxcolors');
528 | return false;
529 | }
530 |
531 | // XXX: check color content and convert to grayscale if insufficient
532 |
533 | var histo = getHisto(pixels),
534 | histosize = 1 << (3 * sigbits);
535 |
536 | // check that we aren't below maxcolors already
537 | var nColors = 0;
538 | histo.forEach(function() { nColors++; });
539 | if (nColors <= maxcolors) {
540 | // XXX: generate the new colors from the histo and return
541 | }
542 |
543 | // get the beginning vbox from the colors
544 | var vbox = vboxFromPixels(pixels, histo),
545 | pq = new PQueue(function(a,b) { return pv.naturalOrder(a.count(), b.count()); });
546 | pq.push(vbox);
547 |
548 | // inner function to do the iteration
549 | function iter(lh, target) {
550 | var ncolors = 1,
551 | niters = 0,
552 | vbox;
553 | while (niters < maxIterations) {
554 | vbox = lh.pop();
555 | if (!vbox.count()) { /* just put it back */
556 | lh.push(vbox);
557 | niters++;
558 | continue;
559 | }
560 | // do the cut
561 | var vboxes = medianCutApply(histo, vbox),
562 | vbox1 = vboxes[0],
563 | vbox2 = vboxes[1];
564 |
565 | if (!vbox1) {
566 | // console.log("vbox1 not defined; shouldn't happen!");
567 | return;
568 | }
569 | lh.push(vbox1);
570 | if (vbox2) { /* vbox2 can be null */
571 | lh.push(vbox2);
572 | ncolors++;
573 | }
574 | if (ncolors >= target) return;
575 | if (niters++ > maxIterations) {
576 | // console.log("infinite loop; perhaps too few pixels!");
577 | return;
578 | }
579 | }
580 | }
581 |
582 | // first set of colors, sorted by population
583 | iter(pq, fractByPopulations * maxcolors);
584 |
585 | // Re-sort by the product of pixel occupancy times the size in color space.
586 | var pq2 = new PQueue(function(a,b) {
587 | return pv.naturalOrder(a.count()*a.volume(), b.count()*b.volume());
588 | });
589 | while (pq.size()) {
590 | pq2.push(pq.pop());
591 | }
592 |
593 | // next set - generate the median cuts using the (npix * vol) sorting.
594 | iter(pq2, maxcolors - pq2.size());
595 |
596 | // calculate the actual colors
597 | var cmap = new CMap();
598 | while (pq2.size()) {
599 | cmap.push(pq2.pop());
600 | }
601 |
602 | return cmap;
603 | }
604 |
605 | return {
606 | quantize: quantize
607 | };
608 | })();
609 |
--------------------------------------------------------------------------------