How to create a "carousel"-like widget in spotify apps API? - spotify

Is it possible using the spotify apps API to create one of these widgets filled with my data of choice?

Yes, by using import/scripts/pager. Here's an example, extracted and simplified from the "What's New" app. Your pager.js:
"use strict";
sp = getSpotifyApi(1);
var p = sp.require('sp://import/scripts/pager');
var dom = sp.require('sp://import/scripts/dom');
exports.init = init;
function init() {
var pagerSection = dom.queryOne('#pager');
var datasource = new DataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
var options = {
perPage: 5,
hidePartials: true,
orientation: 'vertical', // 'vertical', 'horizontal'
pagingLocation: 'top', // 'top', 'bottom'
bullets: false,
listType: 'list', // 'table', 'list'
context: 'aToplist' // some string unique for each pager
};
var pager = new p.Pager(datasource, options);
pager.h2.innerHTML = "Example Pager";
dom.adopt(pagerSection, pager.node);
}
function DataSource(data) {
var data = data;
this.count = function() {
return data.length;
};
this.makeNode = function(index) {
var dataItem = data[index];
var li = new dom.Element('li');
var nameColumn = new dom.Element('div', {
className: 'nameColumn',
html: '<div class="nameColumn">'+
'Name' + dataItem + ''+
'Creator' + dataItem +''+
'</div>'
});
dom.adopt(li, nameColumn);
return li;
};
}
Your index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="pager.css">
</head>
<body onload="sp = getSpotifyApi(1); sp.require('pager').init();">
<div id="wrapper">
<section class="toplists" id="bottomToplists">
<section id="pager" class="playlists playlistsTable toplist"></section>
</section>
</div>
</body>
</html>
And lastly, copy the whatsnew.css into your project and rename it to pager.css. You will of course need to clean up the css and modify the elements in your index.html to fit with your app but this is a good starting point.
The "What's New" app also has an example of a horizontal pager with album artwork. Take a look at this question and answer to figure out how to extract the source of the app.
Also note that I am not sure whether the pager.js will be part of the public API. If not then you can of course extract it into your own pager widget and use it anyway.

Related

Why is my tabulator custom header filter changing on keyup?

To reproduce:
Run the Code Snippet (recommend Full Page mode)
Hold Ctrl or Shift while clicking to select multiple 'species' in the table column header
Note that while the key is held down, the table data is filtered according to selected 'species'
Release the key, observe changes in filtered table data
I think this is probably related to https://github.com/olifolkerd/tabulator/issues/975 and that I need to do something to override default tabulator keypress events.
JSFiddle: https://jsfiddle.net/jjech/3th28pv0/229/
const speciesTypes = [ 'Human', 'Android', 'Betazoid', 'Klingon', 'Ferengi', 'Tamarian' ];
function multiSelectHeaderFilter(cell) {
var values = speciesTypes;
const filterFunc = (rowData) => {
return values.includes(rowData['species']);
}
const getSelectedValues = (multiSelect) => {
var result = [];
var options = multiSelect && multiSelect.options;
var opt;
for (var i=0, iLen=options.length; i<iLen; i++) {
opt = options[i];
if (opt.selected) {
result.push(opt.value || opt.text);
}
}
return result;
}
const onChange = () => {
var editor = document.getElementById('speciesSelector');
values = getSelectedValues(editor);
console.log("values: "+values);
cell.getColumn().getTable().removeFilter(filterFunc);
cell.getColumn().getTable().addFilter(filterFunc);
}
var select = document.createElement("select");
select.multiple = "multiple";
select.id = 'speciesSelector';
select.style = 'width: 100%';
speciesTypes.forEach(species => {
select.innerHTML += "<option id='"+species+"' value='"+species+"' selected='selected'>"+species+"</option>";
});
cell.getColumn().getTable().addFilter(filterFunc);
select.addEventListener('change',onChange);
return select;
}
var table = new Tabulator("#tabulator", {
layout:"fitColumns",
data:[ {name:'Geordi La Forge',species:'Human'}, {name:'Dathon', species:'Tamarian'}, {name:'Jean-Luc Picard', species:'Human'}, {name:'Worf, son of Mogh', species:'Klingon'}, {name:'Tasha Yarr', species:'Human'}, {name:'Data', species:'Android'}, {name:'Wesley Crusher', species:'Human'}, {name:'Jalad', species:'Tamarian'}, {name:'Lwaxana Troi', species:'Betazoid'}, {name:'Temba', species:'Tamarian'}, {name:'T\'Kuvma', species:'Klingon'}, {name:'Lore', species:'Android'}, {name:'Noonian Soongh', species:'Human'}, {name:'Darmok', species:'Tamarian'}, {name:'Reittan Grax', species:'Betazoid'}, {name:'Quark', species:'Ferengi'} ],
headerSort:true,
columns:[ {title:'Name',field:'name',sorter:'string'},{title:'Species',field:'species',sorter:'string',headerFilter:multiSelectHeaderFilter }, ],
});
//document.multiselect('#speciesSelector');
<html>
<head>
<link href="https://unpkg.com/tabulator-tables#4.5.3/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables#4.5.3/dist/js/tabulator.min.js"></script>
<link href="https://cdn.jsdelivr.net/gh/mneofit/multiselect/styles/multiselect.css" rel="stylesheet">
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/mneofit/multiselect/multiselect.min.js"></script>
<script></script>
<style>
.tabulator .tabulator-header,
.tabulator .tabulator-header .tabulator-col
{
overflow: unset;
}
</style>
</head>
<body>
<div id="tabulator"></div>
</body>
</html>
Fixed by adding headerFilterLiveFilter:false to the column definition.
columns:[ {title:'Name',field:'name',sorter:'string'},{title:'Species',field:'species',sorter:'string',headerFilter:multiSelectHeaderFilter,headerFilterLiveFilter:false }, ],
https://jsfiddle.net/jjech/3th28pv0/237/

How to update paginated dgrid periodically

I am trying to display and refresh periodically some server data using dgrid and derivative of Request dstore. The data are paginated and needs to be updated periodically. As a first naive approach I tried to call Dgrid.refresh() with setInterval. However, that results in complete rebuilding of grid rows which visually creates flickering effect. Using Trackable to the store does not help. Can anyone advise me how to refresh rows in the dgrid which would only update changed rows?
Here is my code reproducing the issue:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>DGrid flickering on slow updates</title>
<style>
#import "./dojo-release-1.11.1/dojo/resources/dojo.css";
#import "./dojo-release-1.11.1/dijit/themes/claro/claro.css";
#import "./META-INF/resources/webjars/dgrid/1.0.0/css/dgrid.css";
#import "./META-INF/resources/webjars/dgrid/1.0.0/css/skins/claro.css";
html, body {
padding: 10px;
width: 100%;
height: 100%;
}
</style>
<script>
var dojoConfig = {
async:true,
baseUrl: "./",
packages:[
{ name:"dojo", location:"dojo-release-1.11.1/dojo" },
{ name:"dijit", location:"dojo-release-1.11.1/dijit" },
{ name:"dgrid", location:"META-INF/resources/webjars/dgrid/1.0.0" },
{ name:"dstore", location:"META-INF/resources/webjars/dstore/1.1.1" }
]
};
</script>
<script src="dojo-release-1.11.1/dojo/dojo.js"></script>
<script>
require(["dojo/parser",
"dojo/dom",
"dojo/_base/declare",
"dstore/Store",
"dstore/Trackable",
"dstore/Cache",
"dstore/Memory",
"dstore/QueryResults",
"dojo/Deferred",
"dgrid/Grid",
"dgrid/Keyboard",
"dgrid/Selection",
"dgrid/extensions/Pagination",
"dojo/domReady!"],
function(parser, dom, declare, Store, Trackable, Cache, Memory, QueryResults, Deferred, Grid, Keyboard, Selection, Pagination)
{
parser.parse();
console.log("Parsed");
var makeSlowRequest =
function(kwArgs)
{
var responseDeferred = new Deferred();
var responsePromise = responseDeferred.promise;
// resolve promise in 2 seconds to simulate slow network connection
setTimeout(function ()
{
console.log("Generating response");
var data = {items: [], total: 100};
kwArgs = kwArgs || {start:0, end:100};
for(var i = kwArgs.start; i < kwArgs.end; i++)
{
data.items.push({id: "id-" + i,
name: "test-" + i,
value: Math.floor((Math.random() * 10) + kwArgs.start)
});
}
responseDeferred.resolve(data);
}, 2000);
return new QueryResults(responsePromise.then(function (data) { return data.items; }),
{ totalLength: responsePromise.then(function (data) { return data.total;}) });
};
var SlowStore = declare("SlowStore",
[Store, Trackable],
{ fetch: function (kwArgs) { return makeSlowRequest(kwArgs); },
fetchRange: function (kwArgs) { return makeSlowRequest(kwArgs); }
});
var store = new SlowStore();
var TestGrid = declare([Grid, Keyboard, Selection, Pagination]);
var grid = new TestGrid({
collection: store,
columns: {name: "Name", value: "Value"},
rowsPerPage: 10,
selectionMode: 'single',
cellNavigation: false,
className: 'dgrid-autoheight',
pageSizeOptions: [10, 20],
adjustLastColumn: true
}, dom.byId("grid"));
grid.startup();
// update grid every 5 seconds
var timer = setInterval(function(){console.log("Refreshing grid"); grid.refresh();}, 5000);
});
</script>
</head>
<body class="claro">
<div id="grid"></div>
</body>
</html>
I am guessing that instead of calling refresh I need get the request range from the dgrid (possibly using aspect on gotoPage) and call store.fetchRange(), iterate over the results, compare each item with previous results and invoke store.update or store.add or store.delete. I suppose that would give me what I want but before taking this approach I wondering if there is an easier way to refresh dgrid with updated data. Using Cache store does not work as it expects to fetch all the data from the server:
var store = Cache.create(new SlowStore(), {
cachingStore: new (Memory.createSubclass(Trackable))()
});

template not rendering correctly

Can someone please point me in the right direction. I tried google and didn't find much regarding my issue. While the following code works perfectly fine when running the .html file directly, it doesn't while serving the file in a node express app. The problem is that with node I don't see any data. page loads fine but no data.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="jquery.js"></script>
<script src="handlebars.js"></script>
<script src="underscore.js"></script>
<script src="backbone.js"></script>
<script src="moment.js"></script>
<!-- Setup our templates -->
<script id="PersonTemplate" type="text/x-handlebars-template">
<div>
Person:<br>
{{name}}
{{age}}
</div>
</script>
<!--
Note the [] this is important
because handlebars and backbone collections
dont play well with each other in regards
to naming JSON groups
-->
<script id="PeopleTemplate" type="text/x-handlebars-template">
<div>
People:<br>
{{#each []}}
{{this.name}}
{{this.age}}
<br/>
{{/each}}
</div>
</script>
<!-- End templates setup -->
<script>
// Stub out the person model
var Person = Backbone.Model.extend({
});
// Create a collection of persons
var People = Backbone.Collection.extend({
model: Person
});
// Define the view for a single person
var PersonView = Backbone.View.extend({
render: function() {
// This is method that can be called
// once an object is init. You could
// also do this in the initialize event
var source = $('#PersonTemplate').html();
var template = Handlebars.compile(source);
var html = template(this.model.toJSON());
$(this.el).html(html);
}
});
// Define the view for People
var PeopleView = Backbone.View.extend({
render: function() {
// This is method that can be called
// once an object is init. You could
// also do this in the initialize event
var source = $('#PeopleTemplate').html();
var template = Handlebars.compile(source);
var html = template(this.collection.toJSON());
$(this.el).html(html);
},
initialize: function(){
this.collection.on('add', this.render, this)
}
});
// Create instance of People Collection
var people = new People();
// Create new instances of the person models
var person = new Person({name: "Tim", age: 5});
var person2 = new Person({name: "Jill", age: 15});
// Create instances of the views
var personView = new PersonView({
model: person
});
var peopleView = new PeopleView({
collection: people
});
$(document).ready(function(){
// We have to do this stuff in the dom ready
// so that the container objects get built out
// Set el of the views.
personView.el = $('#personContainer');
peopleView.el = $('#peopleContainer');
// Add them to a new instance of the people collection
people.add(person);
people.add(person2);
// Render the views. If you are using the initialize
// method then you do not have to do this step.
personView.render();
//peopleView.render();
// Try on console!
// people.add(new Person({name: 'Rames', age:'23'}));
});
</script>
</head>
<body>
<div id='personContainer' ></div>
<hr>
<div id='peopleContainer' ></div>
</body>
</html>
Thanks in advance for your help.

how to scale my textlayer

i wish to scale a text layer by button click.
var messageLayer = new Kinetic.Layer();
function writeMessage(message, xx, yy) {
var context = messageLayer.getContext();
context.font = '12pt Calibri';
context.fillStyle = 'white';
wrapText(context, message, xx, yy, seat_size, seat_size);
}
stage.add(messageLayer);
writeMessage(text, xx, yy);
document.getElementById('zoomInBtn').onclick = function zoomInStage(){
stage.setScale(2);
stage.draw();
}
I'm not sure why but this seems not working..
Thanks for helping.
Hi thanks for pointing out my mistake.
but after i add the y value and this is still not working ....
below are my code:
enter <!DOCTYPE HTML>
<html>
<body>
<div id="container"></div>
<button id="scaleme" style="margin-left:10px;">Scale</button>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.0-beta2.js"></script>
<script>
function writeMessage(messageLayer, message) {
var context = messageLayer.getContext();
messageLayer.clear();
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var messageLayer = new Kinetic.Layer();
stage.add(messageLayer);
writeMessage(messageLayer, 'text text text');
document.getElementById('scaleme').onclick = function scale(){
messageLayer.setScale(2,2);
}
</script>
</body>
</html>
please helppp....thanks!
From the API docs:
this is the format:
setScale(x, y)
So you are missing the y parameter it seems.
If you do
stage.setScale(2,2);
then the whole stage will be scaled, but if you want just the text layer to be scaled:
messageLayer.setScale(2,2);
lastly, if you want to be fancy, you can do a transition to zoom to the text:
messageLayer.on('click', function(){
messageLayer.transitionTo({
scale: {
x: 2,
y: 2
},
duration: 1,
easing: 'elastic-ease-out' // this one is not really necessary, just for show
});
});
EDIT:
Since you are using canvas primitives, do this: http://jsfiddle.net/mmqXK/
var context = messageLayer.getContext();
context.scale(1, 0.5);
messageLayer.draw();
writeMessage(messageLayer, 'text text text');

InfoWindow with google maps api

How can I display two Infowindow on my map.
in my code I can open only one InfoWindow, but I will display at same time twoo InfoWindow on my map what can I do. my code is:
function load() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng( 38.736946, 35.310059), 6);
map.openInfoWindow(new GLatLng( 38.582526, 42.846680),
document.createTextNode("Van Gölü"));
}
}
The Google Maps API v2 native InfoWindow only supports one per map. The Google Maps API v3 removes that limitation.
Either use a custom InfoWindow or migrate your application to the Google Maps API v3.
As #duncan observed, that Google Maps API v2 has been officially deprecated as of May 19, 2010 and will not be supported after May 19, 2013. New development in that API is strongly discouraged.
this code is working :
<!DOCTYPE html>
<html>
<head>
<META http-equiv=content-type content=text/html;charset=x-mac-turkish>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map_canvas { height: 100% }
</style>
<script src="js/jquery.js"></script>
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?key=mykey&sensor=false">
</script>
<script type="text/javascript">
var map;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(39.078908,35.244141),
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
infoWindow = new google.maps.InfoWindow();
var windowLatLng = new google.maps.LatLng(38.668356,33.376465);
infoWindow.setOptions({
content: "Tuz Golu",
position: windowLatLng,
});
infoWindow.open(map);
infoWindow2 = new google.maps.InfoWindow();
var windowLatLng2 = new google.maps.LatLng(38.565348,42.868652);
infoWindow2.setOptions({
content: "Van Golu",
position: windowLatLng2,
});
infoWindow2.open(map);
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:65%; height:40%"></div>
</body>
</html>

Resources