Ignore:
Timestamp:
01/15/15 16:01:26 (9 years ago)
Author:
emanuel.dima@uni-tuebingen.de
Message:
  1. alpha11: UI refactor
File:
1 edited

Legend:

Unmodified
Added
Removed
  • SRUAggregator/trunk/src/main/resources/assets/js/main.js

    r5900 r5919  
    55var PT = React.PropTypes;
    66
    7 var SearchBox = window.MyAggregator.SearchBox;
    8 var CorpusSelection = window.MyAggregator.CorpusSelection;
    9 var HitNumber = window.MyAggregator.HitNumber;
    10 var Results = window.MyAggregator.Results;
    11 var CorpusView = window.MyAggregator.CorpusView;
    12 var Modal = window.MyReact.Modal;
    137var ErrorPane = window.MyReact.ErrorPane;
    14 
    15 var multipleLanguageCode = "mul"; // see ISO-693-3
    16 
    17 var layers = [
    18         {
    19                 id: "sampa",
    20                 name: "Phonetics Resources",
    21                 searchPlaceholder: "stA:z",
    22                 searchLabel: "SAMPA query",
    23                 searchLabelBkColor: "#eef",
    24         },
    25         {
    26                 id: "text",
    27                 name: "Text Resources",
    28                 searchPlaceholder: "Elephant",
    29                 searchLabel: "Search text",
    30                 searchLabelBkColor: "#fed",
    31         },
    32 ];
    33 var layerMap = {
    34         sampa: layers[0],
    35         text: layers[1],
    36 };
    37 
    38 function Corpora(corpora, updateFn) {
    39         var that = this;
    40         this.corpora = corpora;
    41         this.update = function() {
    42                 updateFn(that);
    43         };
    44        
    45         var sortFn = function(x, y) {
    46                 var r = x.institution.name.localeCompare(y.institution.name);
    47                 if (r !== 0) {
    48                         return r;
    49                 }
    50                 var t1 = x.title ? x.title : x.displayName;
    51                 var t2 = y.title ? y.title : y.displayName;
    52                 return t1.toLowerCase().localeCompare(t2.toLowerCase());
    53         };
    54 
    55         this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); });
    56         this.corpora.sort(sortFn);
    57 
    58         this.recurse(function(corpus, index) {
    59                 corpus.visible = true; // visible in the corpus view
    60                 corpus.selected = true; // selected in the corpus view
    61                 corpus.expanded = false; // not expanded in the corpus view
    62                 corpus.priority = 1; // priority in corpus view
    63                 corpus.index = index;
    64         });
    65 }
    66 
    67 Corpora.prototype.recurseCorpus = function(corpus, fn) {
    68         if (false === fn(corpus)) {             
    69                 // no recursion
    70         } else {
    71                 this.recurseCorpora(corpus.subCorpora, fn);
    72         }
    73 };
    74 
    75 Corpora.prototype.recurseCorpora = function(corpora, fn) {
    76         var recfn = function(corpus, index){
    77                 if (false === fn(corpus)) {
    78                         // no recursion
    79                 } else {
    80                         corpus.subCorpora.forEach(recfn);
    81                 }
    82         };
    83         corpora.forEach(recfn);
    84 };
    85 
    86 Corpora.prototype.recurse = function(fn) {
    87         this.recurseCorpora(this.corpora, fn);
    88 };
    89 
    90 Corpora.prototype.getLanguageCodes = function() {
    91         var languages = {};
    92         this.recurse(function(corpus) {
    93                 corpus.languages.forEach(function(lang) {
    94                         languages[lang] = true;
    95                 });
    96                 return true;
    97         });
    98         return languages;
    99 };
    100 
    101 Corpora.prototype.isCorpusVisible = function(corpus, layerId, languageCode) {
    102         if (layerId !== "text") {
    103                 return false;
    104         }
    105         // yes for any language
    106         if (languageCode === multipleLanguageCode) {
    107                 return true;
    108         }
    109         // yes if the corpus is in only that language
    110         if (corpus.languages && corpus.languages.length === 1 && corpus.languages[0] === languageCode) {
    111                 return true;
    112         }       
    113 
    114         // ? yes if the corpus also contains that language
    115         if (corpus.languages && corpus.languages.indexOf(languageCode) >=0) {
    116                 return true;
    117         }
    118 
    119         // ? yes if the corpus has no language
    120         // if (!corpus.languages || corpus.languages.length === 0) {
    121         //      return true;
    122         // }
    123         return false;
    124 };
    125 
    126 Corpora.prototype.setVisibility = function(layerId, languageCode) {
    127         // top level
    128         this.corpora.forEach(function(corpus) {
    129                 corpus.visible = this.isCorpusVisible(corpus, layerId, languageCode);
    130                 this.recurseCorpora(corpus.subCorpora, function(c) { c.visible = corpus.visible; });
    131         }.bind(this));
    132 };
    133 
    134 Corpora.prototype.getSelectedIds = function() {
    135         var ids = [];
    136         this.recurse(function(corpus) {
    137                 if (corpus.visible && corpus.selected) {
    138                         ids.push(corpus.id);
    139                         return false; // top-most collection in tree, don't delve deeper
    140                 }
    141                 return true;
    142         });
    143 
    144         // console.log("ids: ", ids.length, {ids:ids});
    145         return ids;
    146 };
    147 
    148 Corpora.prototype.getSelectedMessage = function() {
    149         var selected = this.getSelectedIds().length;
    150         if (this.corpora.length === selected) {
    151                 return "All available collections";
    152         } else if (selected === 1) {
    153                 return "1 selected collection";
    154         }
    155         return selected+" selected collections";
    156 };
    157 
     8var AggregatorPage = window.MyAggregator.AggregatorPage;
    1589
    15910var Main = React.createClass({displayName: 'Main',
     
    16314                        navbarPageFn: this.renderAggregator,
    16415                        errorMessages: [],
    165 
    166                         corpora: new Corpora([], this.updateCorpora),
    167                         languageMap: {},
    16816                };
    169         },
    170 
    171         componentDidMount: function() {
    172                 this.refreshCorpora();
    173                 this.refreshLanguages();
    17417        },
    17518
     
    21457        },
    21558
    216         refreshCorpora: function() {
    217                 this.ajax({
    218                         url: 'rest/corpora',
    219                         success: function(json, textStatus, jqXHR) {
    220                                 this.setState({corpora : new Corpora(json, this.updateCorpora)});
    221                         }.bind(this),
    222                 });
    223         },
    224 
    225         refreshLanguages: function() {
    226                 this.ajax({
    227                         url: 'rest/languages',
    228                         success: function(json, textStatus, jqXHR) {
    229                                 this.setState({languageMap : json});
    230                         }.bind(this),
    231                 });
    232         },
    233 
    234         updateCorpora: function(corpora) {
    235                 this.setState({corpora:corpora});
    236         },
    237 
    23859        renderAggregator: function() {
    23960                return React.createElement(AggregatorPage, {ajax: this.ajax, corpora: this.state.corpora, languageMap: this.state.languageMap});
     
    26485                                                React.createElement("a", {className: "link", tabIndex: "-1",
    26586                                                        onClick: this.setNavbarPageFn.bind(this, this.renderAggregator)}, "Aggregator")
    266                                         ),
    267                                         React.createElement("li", {className: this.state.navbarPageFn === this.renderStatistics ? "active":""},
    268                                                 React.createElement("a", {className: "link", tabIndex: "-1",
    269                                                         onClick: this.setNavbarPageFn.bind(this, this.renderStatistics)}, "Statistics")
    27087                                        ),
    27188                                        React.createElement("li", {className: this.state.navbarPageFn === this.renderHelp ? "active":""},
     
    320137});
    321138
    322 var AggregatorPage = React.createClass({displayName: 'AggregatorPage',
    323         propTypes: {
    324                 ajax: PT.func.isRequired,
    325                 corpora: PT.object.isRequired,
    326                 languageMap: PT.object.isRequired,
    327         },
    328 
    329         mixins: [React.addons.LinkedStateMixin],
    330         timeout: 0,
    331         nohits: {
    332                 requests: [],
    333                 results: [],
    334         },
    335         anyLanguage: [multipleLanguageCode, "Any Language"],
    336 
    337         getInitialState: function () {
    338                 return {
    339                         searchLayerId: "text",
    340                         language: this.anyLanguage,
    341                         numberOfResults: 10,
    342 
    343                         searchId: null,
    344                         hits: this.nohits,
    345                 };
    346         },
    347 
    348         search: function(query) {
    349                 // console.log(query);
    350                 if (!query) {
    351                         this.setState({ hits: this.nohits, searchId: null });
    352                         return;                 
    353                 }
    354                 this.props.ajax({
    355                         url: 'rest/search',
    356                         type: "POST",
    357                         data: {
    358                                 layer: this.state.searchLayerId,
    359                                 language: this.state.language[0],
    360                                 query: query,
    361                                 numberOfResults: this.state.numberOfResults,
    362                                 corporaIds: this.props.corpora.getSelectedIds(),
    363                         },
    364                         success: function(searchId, textStatus, jqXHR) {
    365                                 // console.log("search ["+query+"] ok: ", searchId, jqXHR);
    366                                 this.setState({searchId : searchId});
    367                                 this.timeout = 250;
    368                                 setTimeout(this.refreshSearchResults, this.timeout);
    369                         }.bind(this),
    370                 });
    371         },
    372 
    373         refreshSearchResults: function() {
    374                 if (!this.state.searchId) {
    375                         return;
    376                 }
    377                 this.props.ajax({
    378                         url: 'rest/search/'+this.state.searchId,
    379                         success: function(json, textStatus, jqXHR) {
    380                                 if (json.requests.length > 0) {
    381                                         if (this.timeout < 10000) {
    382                                                 this.timeout = 1.5 * this.timeout;
    383                                         }
    384                                         setTimeout(this.refreshSearchResults, this.timeout);
    385                                         // console.log("new search in: " + this.timeout+ "ms");
    386                                 } else {
    387                                         // console.log("search ended");
    388                                 }
    389                                 this.setState({hits:json});
    390                                 // console.log("hits:", json);
    391                         }.bind(this),
    392                 });
    393         },
    394 
    395         setLanguage: function(languageObj) {
    396                 this.props.corpora.setVisibility(this.state.searchLayerId, languageObj[0]);
    397                 this.setState({language: languageObj});
    398                 this.props.corpora.update();
    399         },
    400 
    401         setLayer: function(layerId) {
    402                 this.props.corpora.setVisibility(layerId, this.state.language[0]);
    403                 this.props.corpora.update();
    404                 this.setState({searchLayerId: layerId});
    405         },
    406 
    407         setNumberOfResults: function(e) {
    408                 var n = e.target.value;
    409                 if (n < 10) n = 10;
    410                 if (n > 250) n = 250;
    411                 this.setState({numberOfResults: n});
    412                 e.preventDefault();
    413                 e.stopPropagation();
    414         },
    415 
    416         stop: function(e) {
    417                 e.preventDefault();
    418                 e.stopPropagation();
    419         },
    420 
    421         toggleCorpusSelection: function(e) {
    422                 $(this.refs.corporaModal.getDOMNode()).modal();
    423                 e.preventDefault();
    424                 e.stopPropagation();
    425         },
    426 
    427         renderAggregator: function() {
    428                 var layer = layerMap[this.state.searchLayerId];
    429                 return  (
    430                         React.createElement("div", {className: "top-gap"},
    431                                 React.createElement("div", {className: "row"},
    432                                         React.createElement("div", {className: "aligncenter", style: {marginLeft:16, marginRight:16}},
    433                                                 React.createElement("div", {className: "input-group"},
    434                                                         React.createElement("span", {className: "input-group-addon", style: {backgroundColor:layer.searchLabelBkColor}},
    435                                                                 layer.searchLabel
    436                                                         ),
    437 
    438                                                         React.createElement(SearchBox, {search: this.search, placeholder: layer.searchPlaceholder}),
    439                                                         React.createElement("div", {className: "input-group-btn"},
    440                                                                 React.createElement("button", {className: "btn btn-default input-lg", type: "button", onClick: this.search},
    441                                                                         React.createElement("i", {className: "glyphicon glyphicon-search"})
    442                                                                 )
    443                                                         )
    444                                                 )
    445                                         )
    446                                 ),
    447 
    448                                 React.createElement("div", {className: "wel", style: {marginTop:20}},
    449                                         React.createElement("div", {className: "aligncenter"},
    450                                                 React.createElement("form", {className: "form-inline", role: "form"},
    451 
    452                                                         React.createElement("div", {className: "input-group", style: {marginRight:10}},
    453                                                                 React.createElement("span", {className: "input-group-addon nobkg"}, "Search in"),
    454                                                                         React.createElement("button", {type: "button", className: "btn btn-default", onClick: this.toggleCorpusSelection},
    455                                                                                 this.props.corpora.getSelectedMessage(), " ", React.createElement("span", {className: "caret"})
    456                                                                         )
    457                                                         ),
    458 
    459                                                         React.createElement("div", {className: "input-group", style: {marginRight:10}},
    460                                                                
    461                                                                 React.createElement("span", {className: "input-group-addon nobkg"}, "of"),
    462                                                                
    463                                                                 React.createElement("div", {className: "input-group-btn"},
    464                                                                         React.createElement("button", {className: "form-control btn btn-default",
    465                                                                                         'aria-expanded': "false", 'data-toggle': "dropdown"},
    466                                                                                 this.state.language[1], " ", React.createElement("span", {className: "caret"})
    467                                                                         ),
    468                                                                         React.createElement("ul", {ref: "languageDropdownMenu", className: "dropdown-menu"},
    469                                                                                 React.createElement("li", {key: this.anyLanguage[0]}, " ", React.createElement("a", {tabIndex: "-1", href: "#",
    470                                                                                                 onClick: this.setLanguage.bind(this, this.anyLanguage)},
    471                                                                                         this.anyLanguage[1])
    472                                                                                 ),
    473                                                                                         _.pairs(this.props.languageMap).sort(function(l1, l2){
    474                                                                                                 return l1[1].localeCompare(l2[1]);
    475                                                                                         }).map(function(l) {
    476                                                                                                 var desc = l[1] + " [" + l[0] + "]";
    477                                                                                                 return React.createElement("li", {key: l[0]}, " ", React.createElement("a", {tabIndex: "-1", href: "#",
    478                                                                                                         onClick: this.setLanguage.bind(this, l)}, desc));
    479                                                                                         }.bind(this))
    480                                                                                
    481                                                                         )
    482                                                                 ),
    483 
    484                                                                 React.createElement("div", {className: "input-group-btn"},
    485                                                                         React.createElement("ul", {ref: "layerDropdownMenu", className: "dropdown-menu"},
    486                                                                                         layers.map(function(l) {
    487                                                                                                 return React.createElement("li", {key: l.id}, " ", React.createElement("a", {tabIndex: "-1", href: "#",
    488                                                                                                         onClick: this.setLayer.bind(this, l.id)}, " ", l.name, " "));
    489                                                                                         }.bind(this))
    490                                                                                
    491                                                                         ),                                                             
    492                                                                         React.createElement("button", {className: "form-control btn btn-default",
    493                                                                                         'aria-expanded': "false", 'data-toggle': "dropdown"},
    494                                                                                 layer.name, " ", React.createElement("span", {className: "caret"})
    495                                                                         )
    496                                                                 )
    497 
    498                                                         ),
    499 
    500                                                         React.createElement("div", {className: "input-group"},
    501                                                                 React.createElement("span", {className: "input-group-addon nobkg"}, "and show up to"),
    502                                                                 React.createElement("div", {className: "input-group-btn"},
    503                                                                         React.createElement("input", {type: "number", className: "form-control input", min: "10", max: "250", step: "5",
    504                                                                                 onChange: this.setNumberOfResults, value: this.state.numberOfResults,
    505                                                                                 onKeyPress: this.stop})
    506                                                                 ),
    507                                                                 React.createElement("span", {className: "input-group-addon nobkg"}, "hits")
    508                                                         )
    509                                                 )
    510                                         )
    511                                 ),
    512 
    513                     React.createElement(Modal, {ref: "corporaModal", title: "Collections"},
    514                                         React.createElement(CorpusView, {corpora: this.props.corpora, languageMap: this.props.languageMap})
    515                     ),
    516 
    517                                 React.createElement("div", {className: "top-gap"},
    518                                         React.createElement(Results, {requests: this.state.hits.requests, results: this.state.hits.results})
    519                                 )
    520                         )
    521                         );
    522         },
    523         render: function() {
    524                 return this.renderAggregator();
    525         }
    526 });
     139
    527140
    528141var StatisticsPage = React.createClass({displayName: 'StatisticsPage',
     
    613226var HelpPage = React.createClass({displayName: 'HelpPage',
    614227        openHelpDesk: function() {
    615                 window.open('http://support.clarin-d.de/mail/form.php?queue=Aggregator',
     228                window.open('http://support.clarin-d.de/mail/form.php?queue=Aggregator&lang=en',
    616229                        '_blank', 'height=560,width=370');
    617230        },
     
    656269});
    657270
    658 var _ = _ || {
    659         keys: function() {
    660                 var ret = [];
    661                 for (var x in o) {
    662                         if (o.hasOwnProperty(x)) {
    663                                 ret.push(x);
    664                         }
    665                 }
    666                 return ret;
    667         },
    668 
    669         pairs: function(o){
    670                 var ret = [];
    671                 for (var x in o) {
    672                         if (o.hasOwnProperty(x)) {
    673                                 ret.push([x, o[x]]);
    674                         }
    675                 }
    676                 return ret;
    677         },
    678 };
    679 
    680 
    681271React.render(React.createElement(Main, null), document.getElementById('reactMain') );
    682272})();
Note: See TracChangeset for help on using the changeset viewer.