Source: widgets/SpwCSWViewer.js

Retour à la documentation
/**
 * @class spw.widgets.SpwCSWViewer
 */
define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/array',
    'dojo/dom-construct',
    'dojo/on',
    'dojo/mouse',
    'dojo/dom-geometry',
    'dojo/dom-style',
    'dojo/store/Memory',
    'dijit/Tooltip',
    'dijit/Dialog',
    'dijit/form/TextBox',
    'dijit/form/ComboBox',
    'dijit/form/NumberSpinner',

    'dojo/text!./templates/SpwCSWViewer.html',

    'spw/api/SpwBaseTemplatedWidget',
    'spw/api/MessageManager',
    'spw/api/SpwCSWCatalog',
    'spw/api/SpwCSWMetawal',

    'dijit/form/Button',
    'dijit/layout/BorderContainer',
    'dijit/layout/ContentPane'
],
function(declare, lang, array, domConstruct, on, mouse, domGeom, domStyle, Memory, Tooltip, Dialog, TextBox,
        ComboBox, NumberSpinner, template, SpwBaseTemplatedWidget, MessageManager, SpwCSWCatalog, SpwCSWMetawal) {

    var SpwCSWViewer = declare('spw.widgets.SpwCSWViewer', [SpwBaseTemplatedWidget], {

        templateString: template,

        // url du service csw
        cswUrl: null,
        // le catalogue utilisé (SpwCSWCatalog ou Metawal)
        catalog: null,

        // pour la pagination
        curPage: null,
        totalPage: null,
        recordsByPage: 20,

        // les options du filtrage (construction formulaire)
        filterOptions: null,
        // contient les infos sur les filtres du formulaire
        filters: null,

        // la combo de tri
        sortSelect: null,

        // le spinner pour le nombre de résultats par page
        recordsByPageBox: null,

        // les filtres à appliquer par défaut
        defaultFilter: null,
        defaultSort: null,

        // propriété correspondant à la description d'un record
        descriptionProperty: 'description',
        
        useCors: true,

        constructor: function() {
        },

        resize: function() {
            this.inherited(arguments);
            this.firstBorderNode.resize();
        },

        postCreate: function() {
            this.inherited(arguments);

            // pour delay le changement de page (le temps que l'utilisateur arrêter de tapper au clavier)
            var delayChangePage = (function(){
                var timer = 0;
                var oldValue = 1;

                return lang.hitch(this, function(callback, page, ms) {
                    if (isNaN(page)) {
                        return;
                    }

                    page = +page;

                    if (oldValue === page) {
                        return;
                    }

                    clearTimeout (timer);
                    oldValue = page;
                    timer = setTimeout(callback, ms, page);
                });
            })();

            this.own(
                on(this.prevPageButton, 'click', lang.hitch(this, function() {
                    this.showCatalog(this.curPage - 1);
                })),

                on(this.firstPageButton, 'click', lang.hitch(this, function() {
                    if (this.curPage === 1) {
                        return;
                    }

                    this.showCatalog(1);
                })),

                on(this.nextPageButton, 'click', lang.hitch(this, function() {
                    this.showCatalog(this.curPage + 1);
                })),

                on(this.lastPageButton, 'click', lang.hitch(this, function() {
                    if (this.curPage === this.totalPage) {
                        return;
                    }

                    this.showCatalog(null);
                })),

                // lorsque l'utilisateur modifie la page par la textbox
                on(this.pageNumberNode, 'keyup', lang.hitch(this, function() {
                    delayChangePage(lang.hitch(this, this.showCatalog),
                                    this.pageNumberNode.get('value'), 1000);
                }))
            );
        },

        onActivate: function() {
            this.inherited(arguments);

            // si le catalog n'est pas instancié
            if (this.catalog == null) {
                // on le fait en se basant sur l'URL pour savoir s'il faut utiliser metawal ou non
                if (this.cswUrl.indexOf('metawal') >= 0) {
                    this.catalog = new SpwCSWMetawal({
                        url: this.cswUrl,
                        defaultFilter: this.defaultFilter,
                        defaultSort: this.defaultSort,
                        useCors: this.useCors
                    });
                }
                else {
                    this.catalog = new SpwCSWCatalog({
                        url: this.cswUrl,
                        defaultFilter: this.defaultFilter,
                        defaultSort: this.defaultSort,
                        useCors: this.useCors
                    });
                }

                // on construit le formulaire de filtrage
                this.drawFilter();
                // et on affiche la première page
                this.showCatalog(1);
            }
        },

        /**
         * Construit le formulaire de filtrage
         */
        drawFilter: function() {

            this.filters = [];
            // pour ne pas ajouter plusieurs fois la même id
            var ids = [];

            array.forEach(this.filterOptions, lang.hitch(this, function(f) {
                if (ids.indexOf(f.field) >= 0) { // id déjà présente ?
                    return;
                }

                // on récupère les infos pour le champs (type, label...)
                var infos = this.catalog.getFilterInfo(f.field);

                if (infos == null) {
                    console.error(f.field + ' is an unknown field')
                    return;
                }

                // on construit la textbox et le label
                var li = domConstruct.create('li', {
                    style: 'margin: 5px 0 5px 0;'
                }, this.filterNode, 'last');

                domConstruct.create('label', {
                    'for': this.widgetId + 'sort' + f.field,
                    innerHTML: infos.label + ' : '
                }, li);

                // TODO: créer en fonction du type (date, numérique...)
                var tmp = new TextBox({
                    id: this.widgetId + 'filter' + f.field,
                    style: 'width: 100%;'
                }, domConstruct.create('input', null, li, 'last'));

                tmp.filterField = f.field;

                this.filters.push(tmp);
            }));

            // création de la combo pour le tri
            var sortItems = [];

            // ajout des paramètres de tri
            array.forEach(this.sortOptions, lang.hitch(this, function(s) {
                var infos = this.catalog.getFilterInfo(s);

                if (infos == null) {
                    return;
                }

                sortItems.push({
                    field: s,
                    label: infos.label
                });
            }));

            if (sortItems.length > 0) {
                var liSort = domConstruct.create('li', {
                    style: 'margin: 20px 0 20px 0;'
                }, this.filterNode, 'last');

                domConstruct.create('label', {
                    'for': this.widgetId + 'sortSelect',
                    innerHTML: 'Trier par : '
                }, liSort);

                this.sortSelect = new ComboBox({
                    id: this.widgetId + 'sortSelect',
                    store: new Memory({data: sortItems}),
                    searchAttr: 'label',
                    style: 'width: 100%;',
                    required: false
                }, domConstruct.create('input', null, liSort));

                this.sortSelect.startup();
            }

            // nombre de records par page
            var liNb = domConstruct.create('li', {
                style: 'margin: 20px 0 20px 0;'
            }, this.filterNode, 'last');

            domConstruct.create('label', {
                'for': this.widgetId + 'recordsByPage',
                innerHTML: 'Nombre de résultats par page : '
            }, liNb);

            this.recordsByPageBox = new NumberSpinner({
                id: this.widgetId + 'recordsByPage',
                value: this.recordsByPage,
                smallDelta: 10,
                constraints: {
                    min: 1,
                    max: 1000,
                    places: 0
                },
                required: true,
                style: 'width: 100%;'
            }, domConstruct.create('input', null, liNb));

            this.recordsByPageBox.startup();

            // rafaîchissement
            this.own(
                on(this.formFilterNode, 'submit', lang.hitch(this, function(evt) {
                    evt.preventDefault();

                    this.showCatalog(1);
                }))
            );
        },

        /**
         * Permet de récupérer le filtre à envoyer au catalogue sur base du formulaire
         * @return {Array} du type [ { field: 'dc:type', like: '%service%' } ]
         */
        getFilter: function() {
            var tmp = [];

            // on parcourt le formulaire
            array.forEach(this.filters, lang.hitch(this, function(f) {
                // si valeur entrée
                if (f.get('value') == null || f.get('value') === '') {
                    return;
                }

                // on push le filtre
                // TODO: eq/neq/gt/gteq/lt/lteq/between
                tmp.push({
                    field: f.filterField,
                    like: f.get('value')
                });
            }));

            // on s'assure qu'on retourne null si aucun filtre
            if (tmp.length > 0) {
                return tmp;
            }

            return null;
        },

        /**
         * Permet de récupérer le paramètre sort à envoyer
         */
        getSort: function() {
            if (this.sortSelect == null) {
                return null;
            }

            var item = this.sortSelect.get('item');

            if (item == null || item === '') {
                return;
            }

            return [{
                field: item.field
            }];
        },

        /**
         * Affiche une page du catalogue
         * @param  {Integer} page numéro de la page (null si dernière)
         */
        showCatalog: function(page) {

            if (page == null) {
                // on récupère la dernière page si elle existe, sinon, on retourne à la première
                if (this.totalPage) {
                    page = this.totalPage;
                }
                else {
                    page = 1;
                }
            }
            else if (page < 1) {
                return;
            }
            else {
                // on vérifie que la page ne dépasse pas le nombre total
                if (this.totalPage) {
                    if (this.totalPage < page) {
                        return;
                    }
                }
            }

            // on construit les options à passer au GetRecords
            var opts = {
                filter: this.getFilter(),
                sort: this.getSort()
            };

            // loader + retour au-dessus de la page (sinon le load ne s'affiche pas bien)
            this.centerNode.domNode.scrollTop = 0;
            this.pageNumberNode.set('disabled', true);
            this.showLoading(this.centerNode.domNode);

            // on calcule le numéro du record de début de page
            this.recordsByPage = this.recordsByPageBox.get('value');
            var start = ((page - 1) * this.recordsByPage) + 1;

            this.catalog.GetRecords(start, this.recordsByPage, opts).then(
                lang.hitch(this, function(data) {
                    // on calcule le nombre total de pages
                    this.totalPage = Math.ceil(data.total / this.recordsByPage);
                    // on met à jour la pagination
                    this.updatePagination(data, page);
                    // on vide la page
                    domConstruct.empty(this.listNode);
                    // et on ajoute les nouveaux éléments à la page
                    array.forEach(data.records, lang.hitch(this, function(r, idx) {
                        var item = new ItemNode({
                            controller: this,
                            record: r
                        }, domConstruct.create('li', null, this.listNode));

                        item.startup();
                    }));

                    this.pageNumberNode.set('disabled', false);
                    this.hideLoading();

                }),
                lang.hitch(this, function(err){
                    MessageManager.getInstance().notifyError(err);
                })
            );
        },

        /**
         * Met à jour le visuel de la pagination
         */
        updatePagination: function(result, page) {
            // le disabled ne fonctionne pas
            if (this.totalPage === page || result.nextRecord === 0) {
                domStyle.set(this.nextPageButton, 'disabled', 'true');
                domStyle.set(this.lastPageButton, 'disabled', 'true');
            }
            else {
                domStyle.set(this.nextPageButton, 'disabled', '');
                domStyle.set(this.lastPageButton, 'disabled', '');
            }

            if (page === 1) {
                domStyle.set(this.prevPageButton, 'disabled', 'true');
                domStyle.set(this.firstPageButton, 'disabled', 'true');
            }
            else {
                domStyle.set(this.prevPageButton, 'disabled', '');
                domStyle.set(this.firstPageButton, 'disabled', '');
            }

            this.curPage = page;

            this.pageNumberNode.set('value', this.curPage);
            this.paginationTextNode.innerHTML = 'sur ' + this.totalPage;
        }

    });

    /**
     * Représente un record du catalogue
     */
    var ItemNode = declare('spw.widgets.SpwCSWViewerItem', [SpwBaseTemplatedWidget], {

        templateString: '<li></li>',

        // controller = SpwCSWViewer
        controller: null,
        // le record
        record: null,

        // paragraphe de description pour qu'il soit coupé si trop long
        clipped: null,
        parClipped: null,

        // le tooltip affichant la description complète
        tooltip: null,

        postCreate: function() {
            this.inherited(arguments);

            if (this.record == null) {
                return;
            }

            this.buildView();
        },

        _getDescription: function() {
            return this.record[this.controller.descriptionProperty] ?
                        this.record[this.controller.descriptionProperty] : 'Aucune description';
        },

        /**
         * Construction de la vue
         */
        buildView: function() {
            // on parcourt les uri du record pour récupérer une éventuelle miniature
            var found = array.some(this.record.uri, lang.hitch(this, function(u) {
                if (u.link.match(/\.(jpg|jpeg|png|gif)$/)) {
                    domConstruct.create('img', {
                        src: u.link,
                        'class': 'iconCsw'
                    }, this.domNode, 'first');
                    return true;
                }

                return false;
            }));

            // si aucune miniature trouvée -> nopreview
            if (!found) {
                domConstruct.create('img', {
                    src: this.imagesPath + 'nopreview.gif',
                    'class': 'iconCsw'
                }, this.domNode, 'first');
            }

            // construction du titre, de la description et des boutons éventuels
            domConstruct.create('h3', {
                style: 'margin-bottom: 0;',
                innerHTML: this.record.title
            }, this.domNode, 'last');

            this.clipped = domConstruct.create('div', {
                'class': 'clipped'
            }, this.domNode, 'last');

            this.parClipped = domConstruct.create('p', {
                innerHTML: this._getDescription()
            }, this.clipped);

            // si le record a un id, on peut en afficher les détails
            if (this.record.id) {
                var details = domConstruct.create('span', {
                    'class': 'details'
                }, this.domNode, 'last');

                this.own(on(details, 'click', lang.hitch(this, function() {
                    var url = this.controller.detailsUrl || '{id}';

                    url = lang.replace(url, {id: this.record.id});

                    if (this.controller.detailsPopup) {
                        var width = this.controller.detailsPopup.width != null ? this.controller.detailsPopup.width : "750px";
                        var height = this.controller.detailsPopup.height != null ? this.controller.detailsPopup.height : "800px";

                        new Dialog({
                            title: '',
                            content: "<iframe width=" + width + " height=" + height + " src=" + url + "></iframe>"
                        }).show();
                    }
                    else {
                        window.open(url, '_blank');
                    }
                })));
            }

            // si le record est un mapservice, on peut l'ajouter à la carte
            if (this.controller.catalog.isMapService(this.record)) {
                var add = domConstruct.create('span', {
                    'class': 'add'
                }, this.domNode, 'last');

                this.own(on(add, 'click', lang.hitch(this, function() {
                    this.controller.catalog.addServiceFromURI(this.record);
                })));
            }

        },

        startup: function() {
            this.inherited(arguments);

            // on calcule la taille du div contenant le paragraphe de description
            // et la taille du paragraphe pour savoir s'il dépasse.
            // Si c'est le cas, on réduit le texte affiché pour qu'il rentre et on
            // ajoute un tooltip pour afficher la description complète
            var divh = domGeom.position(this.clipped).h;
            var addHover = false;

            while (domGeom.position(this.parClipped).h > divh) {
                addHover = true;
                this.parClipped.innerHTML = this.parClipped.innerHTML.replace(/\W*\s(\S)*$/, '...');
            }

            if (addHover) {
                this.tooltip = new Tooltip({
                    connectId: [this.clipped],
                    label: '<div style="max-width: 500px;">' +
                            this._getDescription() + '</div>',
                    position: ['after', 'above'],
                    showDelay: 1000,
                    style: 'width: 500px;'
                });

                this.tooltip.startup();
            }
        }

    });

    return SpwCSWViewer;
});