yaz/commit
first prototype for shot annotation based on user tags
author | Michiel Hildebrand |
---|---|
Fri Oct 28 18:21:46 2011 +0200 | |
committer | Michiel Hildebrand |
Fri Oct 28 18:21:46 2011 +0200 | |
commit | 78b53172f8a2cfab034c033b0111e1fde78a8cea |
tree | 50acb053036e57d79b55fd525f2b3b1c96e2b304 |
parent | 5ee4fd918aa7f55bb5175b2359e3d6c9c1418a49 |
Diff style: patch stat
diff --git a/applications/yaz_game.pl b/applications/yaz_game.pl index 700d8a7..92a37b9 100644 --- a/applications/yaz_game.pl +++ b/applications/yaz_game.pl @@ -42,7 +42,7 @@ 'Interval between requests to the server (in miliseconds)'). :- setting(video_buffer_time, integer, 1, 'Expect delay in load of page and start of Video (in seconds)'). -:- setting(gamestart, oneof([all,creator,none,false]), none, +:- setting(gamestart, oneof([all,creator,none,false]), creator, 'Determines who can start the game'). :- setting(garden, oneof([player,sgarden,mgarden,cgarden,false]), mgarden, 'Redirect to gardening when video is finished'). @@ -53,6 +53,15 @@ :- rdf_meta cond_object_assert(r,r,o,r). +channel(pprime1, + 'https://ecrit10:pPRIME4ever@fileportal.rai.it/da%20RAI%20PRIME/LL/PrestoPRIME_ORF_197668_P0142620.webm', + 'PPrime test 1', + 600). +channel(pprime2, + 'https://ecrit10:pPRIME4ever@fileportal.rai.it/da%20RAI%20PRIME/LL/PrestoPRIME_UIBK_003383.ogv', + 'PPrime test 2', + 1000). + channel('http://nos.nl/uitzending/32556-nos-journaal-1000-uur.html', video('nos_journaal_10022011.flv'), 'NOS journaal 10 Feb 2011', @@ -93,7 +102,7 @@ channel('http://g.bbcredux.com/programme/bbcone/2010-11-21/22-00-00', % Emit a web page with a video tagging game. http_yaz_game(Request) :- - ensure_logged_on(User0), + ensure_logged_on(User0), user_property(User0, url(User)), http_parameters(Request, [ url(URL, @@ -152,7 +161,7 @@ user_tags(Game, User, Tags) :- pairs_values(Tags2, Tags). user_tag(Game, User, Annotation, Tag, Time, Match) :- - rdf(Annotation, pprime:creator, User, Game), + rdf(Annotation, pprime:creator, User, Game), rdf(Annotation, rdf:value, literal(Tag)), rdf(Annotation, pprime:videoPlayhead, literal(Time), _), ( matched_json_term(Annotation, Match) @@ -161,7 +170,7 @@ user_tag(Game, User, Annotation, Tag, Time, Match) :- ). matched_json_term(Annotation, json([score=Score, multiplier=Multiplier])) :- - rdf(Annotation, pprime:score, literal(Score)), + rdf(Annotation, pprime:score, literal(Score)), rdf(Annotation, pprime:multiplier, literal(Multiplier)). @@ -191,7 +200,7 @@ html_home_page(Channels) :- \html_new_game) ]), script(type('text/javascript'), []) - ]). + ]). html_channels([]) --> !. html_channels([channel(URL, Video0, Title)|Vs]) --> @@ -203,7 +212,7 @@ html_channels([channel(URL, Video0, Title)|Vs]) --> Players = [] ), http_link_to_id(http_yaz_game, [url(URL)], Link) - }, + }, html(div(class(channel), [ h4(Title), div(class('thumb-container video'), @@ -266,7 +275,7 @@ html_waiting_page(Game, URL, User, Players) :- div(id(options), [ \html_creator_options(Game, User) ]), - script(type('text/javascript'), + script(type('text/javascript'), \html_waiting_yui(Game, Players, User)) ]). @@ -279,7 +288,7 @@ html_creator_options(Game, User) --> { setting(gamestart, creator), user_process_creator(Game, User), http_link_to_id(http_yaz_game, [game(Game)], Link) - }, + }, html(a(href(Link), 'start the game')). html_creator_options(_, _) --> !. @@ -289,10 +298,10 @@ html_waiting_yui(Game, Players, User) --> http_absolute_location(js('game/players.js'), GamePlayers, []), setting(max_player_count, Max) }, - js_yui3([{modules:{'game-players':{fullpath:GamePlayers}}}], + js_yui3([{modules:{'game-players':{fullpath:GamePlayers}}}], [node,'base','io-base','json-parse','querystring-stringify-simple', 'game-players' - ], + ], [ \js_function_decl(fetchData, [], \[ ' Y.io("',DataServer,'", {data:{game:"',Game,'",user:"',User,'"}, @@ -310,8 +319,8 @@ html_waiting_yui(Game, Players, User) --> emptyShow:symbol(true) })), %\js_call(fetchData) - \js_call('Y.later'(500, symbol('Y'), symbol(fetchData), {}, symbol(true))) - ]). + \js_call('Y.later'(500, symbol('Y'), symbol(fetchData), {}, symbol(true))) + ]). %% html_game_page(+Game, +URL, +User, +PlayHead, +Player, +Tags) @@ -330,13 +339,13 @@ html_game_page(Game, URL, User, PlayHead, Players, Tags) :- ]). html_page_containers(Title) --> - html([ h2(Title), + html([ h2(Title), div(id(main), [ div(id(videoplayer), []), div(class(input), [ input([type(text), id(taginput)]), div(id(suggest), []), - div([id(tags)], []) + div([id(tags)], []) ]) ]), div(id(players), []) @@ -352,10 +361,10 @@ html_page_yui(Game, URL, User, PlayHead, Players, Tags) --> video_source(URL, Video) }, html_requires(js('videoplayer/swfobject.js')), - js_yui3([{modules:{'game-players':{fullpath:GamePlayers}, + js_yui3([{modules:{'game-players':{fullpath:GamePlayers}, 'video-player':{fullpath:VideoPlayer}, 'game-input':{fullpath:GameInput} - }} + }} ], [node,event,widget,anim, 'io-base','json-parse','querystring-stringify-simple', @@ -363,7 +372,7 @@ html_page_yui(Game, URL, User, PlayHead, Players, Tags) --> ], [ \js_new(videoPlayer, 'Y.mazzle.VideoPlayer'({filepath:FilePath, - src:Video, + src:Video, width:640, height:380, controls:symbol(false), @@ -374,7 +383,7 @@ html_page_yui(Game, URL, User, PlayHead, Players, Tags) --> 'Y.mazzle.GameInput'({input:'#taginput', output:'#tags', tags:Tags - })), + })), \js_new(gamePlayers, 'Y.mazzle.GamePlayers'({container:'#players', user:User, @@ -386,7 +395,7 @@ html_page_yui(Game, URL, User, PlayHead, Players, Tags) --> \js_call('videoPlayer.render'('#videoplayer')), \js_yui3_on(gameInput, addTag, addTag), \js_yui3_on(videoPlayer, end, \js_video_end(Garden, Game)) - ]). + ]). js_support_functions(Game, User) --> { http_location_by_id(http_game_add_tag, AddTag), @@ -446,7 +455,7 @@ create_game(URL, Player, Game, _Options) :- create_game(URL, Player, Game, Options) :- option(video(Video), Options, _), option(title(Title), Options, _), - create_user_process(Player, + create_user_process(Player, [rdf:type=pprime:'Game', opmv:used=URL ], Game), @@ -487,7 +496,7 @@ game_video_start(_Game, '', 0). % for testing % Asserts that Player has joined game. join_game(Game, Player) :- - ( user_process_joined(Game, Player) + ( user_process_joined(Game, Player) -> set_active_process(Game) ; join_user_process(Game, Player), debug(game, 'Game ~w joined by ~w', [Game, Player]), @@ -501,12 +510,12 @@ join_game(Game, Player) :- add_tag(Game, Player, Tag, PlayHead, AnnotationId) :- rdf(Game, opmv:used, URL), - create_video_annotation(URL, literal(Tag), PlayHead, Player, AnnotationId), + create_video_annotation(URL, literal(Tag), PlayHead, Player, AnnotationId), debug(game, 'Added tag ~w at time ~w by ~w (~w)', [Tag, PlayHead, Player, AnnotationId]). /******************************* - * data services * + * data services * *******************************/ :- http_handler(yaz('waitingdata'), http_waiting_data, []). @@ -519,10 +528,10 @@ add_tag(Game, Player, Tag, PlayHead, AnnotationId) :- % Return JSON object with waiting players in game. http_waiting_data(Request) :- - http_parameters(Request, + http_parameters(Request, [ game(Game, [uri, description('URL of current Game')]) - ]), + ]), ( rdf(Game, opmv:wasStartedAt, _) -> reply_json(json([start= @true])) ; PlayerObj = json([player=P, name=N]), @@ -541,7 +550,7 @@ http_waiting_data(Request) :- % Return JSON object with players in a game. http_game_add_tag(Request) :- - http_parameters(Request, + http_parameters(Request, [ game(Game, [uri, description('URL of current Game')]), user(User, @@ -552,11 +561,11 @@ http_game_add_tag(Request) :- [description('Optionally a new tag can be added')]) ]), setting(match_interval, Interval), - add_tag(Game, User, Tag, Playhead, Id), + add_tag(Game, User, Tag, Playhead, Id), matching_tags(Game, User, Id, Tag, Playhead, Interval, Matches), length(Matches, C), rdf_transaction(update_scores(Matches, 0, C, Game)), - reply_json(json([id=Id])). + reply_json(json([id=Id])). %% match_existing_tags(+Game, +User, +Tag, +Playhead, +Interval, @@ -578,7 +587,7 @@ matching_tags(Game, User, Id, Tag, Playhead, Interval, Matches) :- matching_tag(Game, User, Tag, Playhead, Interval, Id, Player, Match, Time) :- Start is Playhead-Interval, tag_match(Tag, Game, Id, Match), - rdf(Id, pprime:videoPlayhead, literal(between(Start,Playhead), Time)), + rdf(Id, pprime:videoPlayhead, literal(between(Start,Playhead), Time)), rdf(Id, pprime:creator, Player), User \== Player. @@ -594,13 +603,13 @@ update_scores([], _, _, _). update_scores([match(Id,Player,Match)|Ms], N, Count, Game) :- N1 is N + 1, match_score(Match, N1, Count, Points, Multiplier), - update_tag_score(Id, Game, Points, Multiplier), + update_tag_score(Id, Game, Points, Multiplier), update_player_score(Game, Player, Points), update_scores(Ms, N1, Count, Game). update_player_score(Game, Player, Points) :- player_score(Game, Player, OldScore), - retractall(player_score(Game, Player, OldScore)), + retractall(player_score(Game, Player, OldScore)), NewScore is OldScore+Points, assert(player_score(Game, Player, NewScore)). @@ -615,9 +624,9 @@ order_count_multiplier(Order, Count, X) :- X is 4/(Order*2) + ((Count-2)/2). update_tag_score(Annotation, Game, Score, Multiplier) :- - rdf_retractall(Annotation, pprime:score, _), + rdf_retractall(Annotation, pprime:score, _), rdf_retractall(Annotation, pprime:multiplier, _), - rdf_assert(Annotation, pprime:score, literal(Score), Game), + rdf_assert(Annotation, pprime:score, literal(Score), Game), rdf_assert(Annotation, pprime:multiplier, literal(Multiplier), Game). @@ -626,14 +635,14 @@ update_tag_score(Annotation, Game, Score, Multiplier) :- % Return JSON object with players in a game. http_game_data(Request) :- - http_parameters(Request, + http_parameters(Request, [ game(Game, [uri, description('URL of current Game')]), user(User, [uri, description('URL of current User')]), playhead(Playhead, [number, description('Current time of the video play head (in miliseconds)')]) - ]), + ]), setting(match_interval, Interval), PlayerObj = json([player=P, name=Name, score=Score]), findall(PlayerObj, @@ -652,9 +661,9 @@ http_game_data(Request) :- user_matched_tag(Game, User, Playhead, Interval, Id, Tag, Time, Match) :- Start is Playhead-Interval, - rdf(Id, pprime:videoPlayhead, literal(between(Start,Playhead), Time), Game), + rdf(Id, pprime:videoPlayhead, literal(between(Start,Playhead), Time), Game), rdf(Id, pprime:creator, User), - matched_json_term(Id, Match), + matched_json_term(Id, Match), rdf(Id, rdf:value, literal(Tag)). @@ -671,7 +680,7 @@ assert_channel_info :- rdf_transaction(( channel(URL, Video0, Title, Duration), http_absolute_location(Video0, Video, []), add_video(URL, Video, Title, Duration), - fail + fail ; true )). @@ -681,7 +690,8 @@ assert_channel_info :- add_video(URL, Video, Title, Duration) :- %rdf_retractall(URL, _, _), - cond_object_assert(URL, pprime:source, Video, video), + cond_object_assert(URL, rdf:type, pprime:'Video', video), + cond_object_assert(URL, pprime:source, Video, video), cond_object_assert(URL, dc:title, literal(Title), video), cond_object_assert(URL, pprime:duration, literal(Duration), video). diff --git a/applications/yaz_shot_annotation.pl b/applications/yaz_shot_annotation.pl new file mode 100644 index 0000000..6aba072 --- /dev/null +++ b/applications/yaz_shot_annotation.pl @@ -0,0 +1,225 @@ +:- module(yaz_shot_annotation, + []). + +:- use_module(library(http/http_dispatch)). +:- use_module(library(http/http_parameters)). +:- use_module(library(http/http_path)). +:- use_module(library(http/html_write)). +:- use_module(library(http/html_head)). +:- use_module(library(http/http_json)). +:- use_module(library(http/js_write)). +:- use_module(library(http/json)). +:- use_module(library(http/http_session)). +:- use_module(user(user_db)). +:- use_module(library(semweb/rdf_db)). +:- use_module(library(semweb/rdfs)). +:- use_module(library(semweb/rdf_label)). + +:- use_module(library(yaz_util)). +:- use_module(library(yui3)). +:- use_module(library(video_annotation)). + +:- use_module(applications(yaz_tag)). +:- use_module(components(label)). +:- use_module(components(yaz_page)). +:- use_module(components(yaz_video_item)). + +:- use_module(library(rdf_history)). +:- use_module(library(user_process)). + +:- http_handler(yaz(shot), http_yaz_shot, []). + + + +reconcile_source(gtaa, + 'Person', + Server, + Params) :- + http_location_by_id(http_reconcile, Server), + www_form_encode('[{"http://www.w3.org/2004/02/skos/core#inScheme":"http://data.beeldengeluid.nl/gtaa/GTAA"}]',Ps), + atom_concat('&properties=',Ps,Params). +reconcile_source(cornetto, + 'Subject', + Server, + '&type=http://purl.org/vocabularies/cornetto/Synset') :- + http_location_by_id(http_reconcile, Server). +reconcile_source(geonames, + 'Location', +'http://api.kasabi.com/api/reconciliation-api-geonames', + '&apikey=908177a484aa25f9b602d3eb76cf057d73e7aa39'). +%reconcile_source(dbpedia, +% 'DBPedia', +%'http://api.kasabi.com/api/reconciliation-api-dbpedia-36', +% '&apikey=908177a484aa25f9b602d3eb76cf057d73e7aa39'). +%reconcile_source(freebase, +% 'Freebase', +% 'http://standard-reconcile.freebaseapps.com/reconcile', +% ''). + + + +%% http_yaz_shot(+Request) +% +% Emit an HTML page to link tags to concepts. + +http_yaz_shot(Request) :- + http_parameters(Request, + [ video(Video, + [description('Current video')]) + ]), + video_annotations(Video, As0, []), + sort_by_arg(As0, 2, Annotations), + findall(Id=json([label=Label,url=URL,parameters=Parameters]), + reconcile_source(Id,Label,URL,Parameters), Sources), + html_page(Video, Annotations, json(Sources)). + + +%% html_page(+Video, +Annotations) +% +% Emit an HTML page for concept gardening + +html_page(Video, Annotations, Sources) :- + reply_html_page(yaz, + [ title(['YAZ - ', Video]) + ], + [ \html_requires(yui3('cssgrids/grids-min.css')), + \html_requires(css('shotgarden.css')), + \yaz_video_header(Video), + div(class('yui3-g'), + [ div([class('yui3-u'), id(nav)], + [ div(class(hd), '1. Select shot'), + div(id(videoframes), []) + ]), + div([class('yui3-u'), id(main)], + [ div(class(hd), '2. View content'), + div(id(timeline), []), + div(id(videoplayer), []) + ]), + div([class('yui3-u'), id('select-tag')], + [ div(class(hd), '3. Select relevant tags'), + div(id(taglist), []) + ]), + div([class('yui3-u'), id('select-concept')], + [ div(class(hd), '4. Select concept'), + div(id(taglinker), []) + ]) + ]), + script(type('text/javascript'), + \html_video_page_yui(Video, Annotations, Sources)) + ]). + +html_annotation_form --> + html(table([ tr([td(class(label), 'Who'), + td(input([type(text)])) + ]), + tr([td(class(label), 'Where'), + td(input([type(text)])) + ]), + tr([td(class(label), 'When'), + td(input([type(text)])) + ]) + ])). + + +html_video_page_yui(Video, Annotations, Sources) --> + { video_source(Video, Src, Duration), + http_location_by_id(serve_video_frame, FrameServer), + http_absolute_location(js('videoplayer/'), FilePath, []), + http_absolute_location(js('videoplayer/videoplayer.js'), Videoplayer, []), + http_absolute_location(js('timeline/timeline.js'), Timeline, []), + http_absolute_location(js('videoframes/videoframes.js'), Videoframes, []), + http_absolute_location(js('tagplayer/taglist.js'), Taglist, []), + http_absolute_location(js('tagplayer/tagLinker.js'), TagLinker, []), + annotation_to_json(Annotations, JSONTags), + Frames = [{startTime:2000},{startTime:10000},{startTime:20000}, + {startTime:30000},{startTime:40000},{startTime:50000}] + }, + html_requires(js('videoplayer/swfobject.js')), + js_yui3([{modules:{'video-player':{fullpath:Videoplayer}, + 'timeline':{fullpath:Timeline}, + 'video-frames':{fullpath:Videoframes}, + 'tag-list':{fullpath:Taglist}, + 'tag-linker':{fullpath:TagLinker} + }} + ], + [node,event,widget,anim, + 'json','jsonp','querystring-stringify-simple',io, + 'video-player','video-frames',timeline, + 'tag-list','tag-linker' + ], + [ \js_new(videoPlayer, + 'Y.mazzle.VideoPlayer'({filepath:FilePath, + src:Src, + width:540, + height:400, + autoplay:symbol(false), + controls:symbol(true), + duration:Duration + })), + \js_new(timeline, + 'Y.mazzle.Timeline'({height:20, + width:540, + duration:Duration, + items:JSONTags + })), + \js_new(videoFrames, + 'Y.mazzle.VideoFrames'({frameServer:FrameServer, + video:Src, + width:200, + height:600, + frames:Frames, + interval:0, + duration:Duration, + playerPath:FilePath, + confirm:symbol(false), + showRelated:symbol(false), + showTime:symbol(true) + })), + \js_new(tagList, + 'Y.mazzle.TagList'({tags:JSONTags, + height:425, + width:220 + })), + \js_new(tagLinker, + 'Y.mazzle.TagLinker'({sources:Sources, + width:220 + })), + \js_yui3_render(videoPlayer, #(videoplayer)), + \js_yui3_render(timeline, #(timeline)), + \js_yui3_render(videoFrames, #(videoframes)), + \js_yui3_render(tagList, #(taglist)), + \js_yui3_render(tagLinker, #(taglinker)), + \js_yui3_on(videoFrames, frameSelect, \js_frame_select), + \js_yui3_on(tagList, itemSelect, \js_tag_select), + \js_yui3_on(tagLinker, submit, \js_concept_select), + \js_yui3_on(tagLinker, cancel, \js_concept_cancel) + ]). + +js_frame_select --> + js_function([e], + \[ +' var frame = e.frame; + var time = (frame.startTime/1000); + videoPlayer.setTime(time, true);\n' + ]). + +js_tag_select --> + js_function([e], + \[ +' Y.one("#select-tag").addClass("hidden"); + Y.one("#select-concept").removeClass("hidden"); + tagLinker.set("tag", e.tag.tag.value);\n' + ]). +js_concept_cancel --> + js_function([e], + \[ +' Y.one("#select-concept").addClass("hidden"); + Y.one("#select-tag").removeClass("hidden");\n' + ]). +js_concept_select --> + js_function([e], + \[ +' console.log(e.concept); + Y.one("#select-concept").addClass("hidden"); + Y.one("#select-tag").removeClass("hidden");\n' + ]). diff --git a/lib/yaz_util.pl b/lib/yaz_util.pl index 14862f3..2a2fccc 100644 --- a/lib/yaz_util.pl +++ b/lib/yaz_util.pl @@ -323,7 +323,10 @@ delete_nonground([_H|T], Rest) :- video_source(URL, Video, Duration) :- video_source(URL, Video), ( rdf(URL, pprime:duration, literal(Duration0)) - -> atom_number(Duration0, Duration1), + -> ( number(Duration0) + -> Duration1 = Duration0 + ; atom_number(Duration0, Duration1) + ), Duration is Duration1*1000 ; Duration = 0 ). diff --git a/web/css/shotgarden.css b/web/css/shotgarden.css new file mode 100644 index 0000000..4a87078 --- /dev/null +++ b/web/css/shotgarden.css @@ -0,0 +1,179 @@ +#body { + margin: 0 auto; +} + +/* header and description */ +.video-results h2 { + margin-bottom: 0; +} +.video-results .desc { + margin-bottom: 15px; + max-height: 2em; + color: #888; + font-size: 95%; + clear: both; +} + +/* general page elements */ +.hd { + padding: 4px; + font-weight: bold; + border-width: 1px 1px 0 1px; + border-style: solid; + border-color: #CCC; + background-color: #EEE; +} + + + +/* page layout */ + +#nav { + width: 200px; +} +#main { + width: 540px; + margin: 0 10px; +} +#select-tag { + width: 220px; +} +#select-concept { + width: 220px; +} +#select-tag.hidden, +#select-concept.hidden { + display:none; +} + + + +/* video frames */ +.yui3-video-frames { +} +.yui3-video-frames-content { + overflow: auto; + border: 1px solid #CCC; +} +.yui3-video-frames .frames-list { +} +.yui3-video-frames-disabled { + display: none; +} +.yui3-video-frames ul.frames-list { + margin: 0; + padding: 0; +} +.yui3-video-frames li { + overflow: hidden; + list-style: none; + border: 4px solid transparent; +} +.yui3-video-frames li.selected { + border-color: yellow; +} +.yui3-video-frames li.hidden { + display: none; +} +.yui3-video-frames img { + width: 190px; +} +.yui3-video-frames .image { + overflow: hidden; +} +.yui3-video-frames .tag { + text-align: center; + padding: 3px 0; + background-color: #DDD; + cursor: pointer; +} + + +/* timeline */ +.yui3-timeline { + background-color:#CCCCCC; + height:15px; + width:100%; +} +.yui3-timeline ul { + margin: 0; + padding: 0; +} +.yui3-timeline li { + list-style: none; + position: absolute; + margin: 1px 0; + height: 18px; + background-color:yellow; +} +.yui3-timeline li.hidden { + display: none; +} +.yui3-timeline li.highlight { + background-color:red; +} + + +/* tag player */ +.yui3-tag-list { + background: transparent; + overflow: auto; + border: 1px solid #CCCCCC; +} +.yui3-tag-list ul { + margin: 0; + padding: 0; +} +.yui3-tag-list li { + overflow: hidden; + list-style: none; + margin: 1px 0; + padding: 4px 8px; +} +.yui3-tag-list li:nth-child(even) { + background-color: #EEE; +} +.yui3-tag-list li.focus .label { + font-size: 150%; +} +.yui3-tag-list li .label { + cursor: pointer; +} +.yui3-tag-list li .label.concept { + color: #0033CC; +} + + +/* tag linker */ + +.yui3-tag-linker { + background: transparent; +} +.yui3-tag-linker-content { + overflow: auto; + height: 398px; + border: 1px solid #CCCCCC; +} +.yui3-tag-linker .sources { + height: 370px; +} +.yui3-tag-linker .controls { + height: 25px; +} +.yui3-tag-linker .controls button { + width: 50%; + height: 25px; + border: none; +} +.yui3-tag-linker ul { + margin: 0; + padding: 0; +} +.yui3-tag-linker li { + list-style: none; + margin: 1px 0; + padding: 4px 8px; +} +.yui3-tag-linker li.hidden { + display: none; +} \ No newline at end of file diff --git a/web/js/tagplayer/tagLinker.js b/web/js/tagplayer/tagLinker.js index 3e726a4..3511bb9 100644 --- a/web/js/tagplayer/tagLinker.js +++ b/web/js/tagplayer/tagLinker.js @@ -25,15 +25,17 @@ YUI.add('tag-linker', function(Y) { * as with any other class extending Base. */ TagLinker.ATTRS = { - items: { - value: [] + tag: { + value: null + }, + sources: { + value: {} + }, + limit: { + value: 3 } }; - /* Static constants used to define the markup templates used to create TagLinker DOM elements */ - TagLinker.LIST_CLASS = 'tag-list'; - TagLinker.LIST_TEMPLATE = '<ul class="'+TagLinker.LIST_CLASS+'"></ul>'; - /* TagLinker extends the base Widget class */ Y.extend(TagLinker, Widget, { @@ -45,56 +47,112 @@ YUI.add('tag-linker', function(Y) { renderUI : function() { var content = this.get("contentBox"); - this.listNode = content.appendChild(Node.create(TagLinker.LIST_TEMPLATE)); - var controls = content.appendChild('<div class="controls hidden"></div>'); - this.applyButton = controls.appendChild('<button>apply</button>'); - this.applyAllButton = controls.appendChild('<button>apply to all</button>'); - this.controls = controls; + + var tagNode = content.appendChild(Node.create('<div class="tag"></div>')); + var linkBox = content.appendChild(Node.create('<div class="sources"></div>')); + var controls = content.appendChild('<div class="controls"></div>'); + this.cancelButton = controls.appendChild('<button>cancel</button>'); + this.submitButton = controls.appendChild('<button>submit</button>'); + this._renderSources(linkBox); + this.tagNode = tagNode; }, bindUI : function() { - this.after("itemsChange", this.syncUI); - this.applyButton.on("click", this._applySelect, this, {"applyToAll":false}); - this.applyAllButton.on("click", this._applySelect, this, {"applyToAll":true}); + this.after("tagChange", this.syncUI); + this.cancelButton.on("click", this._cancel, this); + this.submitButton.on("click", this._submit, this); }, syncUI : function() { - this._renderItems(); + var tag = this.get("tag"); + if(tag) { + this.tagNode.setContent(tag); + this._fetchConcepts(); + } else { + this.tagNode.setContent(""); + // hide source results + } }, - _applySelect : function(e, args) { - var items = this.get("items"), - checked = this.listNode.one("li input:checked"); - if(checked) { - var index = this.listNode.all("li").indexOf(checked.get("parentNode")); - this.fire("applySelect", { - item:items[index], - applyToAll:args.applyToAll - }) - } + _cancel: function() { + var tag = this.get("tag"); + this.fire("cancel", {"tag":tag}); + }, + + _submit: function() { + var tag = this.get("tag"), + selected = this.get("contentBox").one("input[name=reconcileItem]:checked"), + concept = selected.get("value"); + if(concept) { + this.fire("submit", {"tag":tag, "concept":concept}); + } + }, - _renderItems : function() { - var items = this.get("items"), - list = this.listNode; - list.setContent(""); - if(items.length>0) { - for (var i=0; i < items.length; i++) { - list.append("<li>"+this.formatItem(items[i])+"</li>"); - } - this.controls.removeClass("hidden"); - } else { - this.controls.addClass("hidden"); + _renderSources : function(node) { + var sources = this.get("sources"); + for(var key in sources) { + var source = sources[key], + label = source.label, + img = source.img; + + var box = node.appendChild(Node.create('<div class="source"></div>')); + box.appendChild('<div class="source-hd"><img src="'+img+'" alt="'+label+'"></div>'); + var bd = box.appendChild(Node.create('<div class="source-bd"></div>')); + source._list = bd.appendChild(this._renderSourceList()); } }, + _renderSourceList : function() { + var limit = this.get("limit"); + var listNode = Node.create('<ul></ul>'); + for (var i=0; i < limit; i++) { + listNode.appendChild("<li class='hidden'></li>"); + }; + return listNode; + }, + + _fetchConcepts : function() { + var tag = this.get("tag"), + sources = this.get("sources"), + limit = this.get("limit"); + + for(var key in sources) { + var source = sources[key], + url = source.url + + "?callback={callback}" + + source.parameters + + "&query="+tag + + "&limit="+limit; + Y.log('reconcile against '+source.label); + this.fire("reconcileStart", {"source":source}); + this.reconcile(url, source); + } + }, + + reconcile : function(request, source) { + var oSelf = this, + items = source._list.all("li"); + Y.jsonp(request, function(response) { + items.each(function(node, i) { + var results = response.result; + if(results[i]) { + node.setContent(oSelf.formatItem(results[i])); + node.removeClass("hidden"); + } else { + node.addClass("hidden"); + } + }) + }) + }, + formatItem : function(item) { var id = item.id, name = item.name, types = item.type||[]; - var html = "<input type=radio name='reconcileItem'>"; + var html = "<input type=radio name='reconcileItem' value='"+id+"'>"; html += "<span class='name'>"+name+"</span>"; html += "<div class='types'>"; for (var i=0; i < types.length; i++) { diff --git a/web/js/tagplayer/taglist.js b/web/js/tagplayer/taglist.js index a89dba8..92f812f 100644 --- a/web/js/tagplayer/taglist.js +++ b/web/js/tagplayer/taglist.js @@ -49,7 +49,7 @@ YUI.add('tag-list', function(Y) { renderUI : function() { var content = this.get("contentBox"), height = this.get("height"); - + // tag list content.setStyle("position", "relative"); if(this.get("topIndent")) { diff --git a/web/js/videoframes/videoframes.js b/web/js/videoframes/videoframes.js index b4269e7..e14f1e2 100644 --- a/web/js/videoframes/videoframes.js +++ b/web/js/videoframes/videoframes.js @@ -138,7 +138,7 @@ YUI.add('video-frames', function(Y) { if(frames[i]) { var startTime = frames[i].startTime; node.setContent(this.formatFrame(frames[i])); - if(startTime>time+interval) { + if(time==0 || startTime>time+interval) { node.removeClass("hidden"); } time = startTime; @@ -153,7 +153,7 @@ YUI.add('video-frames', function(Y) { var frameServer = this.get("frameServer"), video = this.get("video"), time = frame.startTime/1000, - label = frame.tag.label, + label = frame.tag ? frame.tag.label : '', role = frame.role ? frame.role : ''; var html = '<div class="image">'