/**
 * BQ.data.Content
 * This is the record def'n for all BQMContent data (Video, Picture, Album)
 */
BQ.data.Content = Ext.data.Record.create([
    {name: 'id', mapping: 'id'},
    {name: 'title', mapping: 'title'},
    {name: 'description', mapping: 'description'},
    {name: 'ownerId', mapping: 'ownerId'},
    {name: 'owner', mapping: 'ownerUsername'},
    {name: 'time_created', mapping:'_time_created'},
    {name: 'rating', mapping: 'rating'},
    {name: 'vote', mapping:'vote'},
    {name: 'type', mapping: 'type'},
    {name: 'thumb_small', mapping: 'thumb_small'},
    {name: 'thumb_square', mapping: 'thumb_square'},
    {name: 'thumb_heart', mapping: 'thumb_heart'},
    {name: 'thumb_preview', mapping: 'thumb_preview'},
    {name: 'thumb_normal', mapping: 'thumb_normal'},
    {name: 'file', mapping: 'file'},
    {name: 'tags', mapping: 'tags'},
    {name: 'comments', mapping: 'comments'},
    {name: 'status', mapping:'status'},
    {name: 'view', mapping:'view'},
    /**
    * Vincent Maillot vincent.maillot@cossette.com
    * 09/10/07
    * Ticket 6030 : nb max de photos par album - ajout du nb actuel de photos dans un album / null si type != album
    **/
    {name:'nbChildren', mapping:'nbChildren'}
]);

/***
 * BQ.data.Photo
 * use for views and combos, api call User/json/BQAStory/listMyPhotos
 */
BQ.data.Photo = Ext.data.Record.create([
    {name: 'id', mapping: 'id'},
    {name: '_slug', mapping: '_slug'},
    {name: '_time_created', mapping: '_time_created'},
    {name: 'regions', mapping: 'regions'},
    {name: 'categories', mapping: 'categories'},
    {name: 'seasons', mapping: 'seasons'},
    {name: 'title', mapping: 'title'},
    {name: 'description', mapping: 'description'},
    {name: 'status', mapping: 'status'},
    {name: 'comments', mapping: 'comments'},
    {name: 'heart', mapping: 'heart'},
    {name: 'view', mapping: 'view'},
    {name: 'rating', mapping: 'rating'},
    {name: 'vote', mapping: 'vote'},
    {name: 'type', mapping: 'type'},
    {name: 'ownerId', mapping: 'ownerId'},
    {name: 'file', mapping: 'file'},
    {name: 'encoded', mapping: 'encoded'},
    {name: 'thumb_small', mapping: 'thumb_small'},
    {name: 'thumb_square', mapping: 'thumb_square'},
    {name: 'thumb_heart', mapping: 'thumb_heart'},
    {name: 'thumb_preview', mapping: 'thumb_preview'},
    {name: 'thumb_normal', mapping: 'thumb_normal'},
    {name: 'ownerUsername', mapping: 'ownerUsername'},
    {name:'view', mapping:'view'}
]);

/***
 * BQ.data.Video
 * use for views and combos, api call User/json/BQAStory/listMyVideos
 */
BQ.data.Video = Ext.data.Record.create([
    {name: 'id', mapping: 'id'},
    {name: 'title', mapping: 'title'},
    {name: 'historyId', mapping: 'historyId'},
    {name: 'file', mapping: 'file'},
    {name: 'encoded', mapping: 'encoded'},
    {name: 'thumb_small', mapping: 'thumb_small'},
    {name: 'thumb_square', mapping: 'thumb_square'},
    {name: 'thumb_heart', mapping: 'thumb_heart'},
    {name: 'thumb_preview', mapping: 'thumb_preview'},
    {name: 'thumb_normal', mapping: 'thumb_normal'}
]);

/**
 * BQ.content.TemplateMethods
 * TODO: re-namespace as BQ.helper.Content
 * Helper methods for rendering XTemplate.
 * <code>
 *      <div>{[ this.ratingToClassName(3) ]}</div>
 * </code>
 * @singleton
 * @author Chris Scott
 */
BQ.content.TemplateMethods = function(){
    var albumCoverId = null;
    return {
        /**
         * ratingToClassName
         * @author Jesse Sutherland
         * Converts an Integer rating to a String classNmae
         * @param {Integer} rating
         * @return {String}
         */
        ratingToClassName: function(rating){
            rating=parseInt(rating);
            var className = '';
            switch (rating) {
                case 1:
                    className='one';
                    break;
                case 2:
                    className='two';
                    break;
                case 3:
                    className='three';
                    break;
                case 4:
                    className='four';
                    break;
                case 5:
                    className='five';
                    break;
                default:
                    className='none';
                    return className;
            }
            return className +'_on';
        },

        /**
         * isGeoTaggable
         * only certain types of content are geotaggable
         * @param {String} content
         */
        isGeotaggable : function (content) {
            var result = false;
            switch (content) {
                case "album":
                case "story":
                    break;
                default:
                    result = true;
                    break;
            }
            return result;
        },

        /**
         * isAlbum
         * if the type is an album return true to help the template render a Map or not...
         * @param {String} type
         * @author: D. Lazar
         */
        isAlbum: function (type) {
            return type == 'album';
        },

        /**
         * getStatus
         * return the status for the template, translated
         * @param {String} status
         */
        getStatus: function (status) {
            return App.t(status);
        },

        /**
         * reviewerExists
         * if a reviewer exists for a piece of content we return true
         * @param {Object} reviewer
         */
        reviewerExists: function (reviewer) {
            return (typeof reviewer != 'undefined')? true : false;
        },

        /**
         * formatDate
         * given a date, format it to look nicer on the site
         * @param {Object} dt
         */
        formatDate: function(dt) {
            return Date.parseDate(dt, "Y-m-d H:i:s").format("Y-m-d");
        },


        /**
         * formatVotes
         * depending on the value of VT assign a plural or singular vote
         * @param {Number} vt
         */
        formatVotes: function(vt) {

            var jLang = BQ.content.TemplateMethods.getLang();

            if (vt == 1 || (vt == 0 && jLang !== 'en')) { //singular rules for both and zero in FR
                return (vt + ' vote');
            }

            if (vt !== 1) { //plural same in either lang, except for zero taken care of above
                return (vt + ' votes');
            }
        },

        /**
         * urlFor
         * returns a string based on the user, the content and the content id for the router
         * @param {Ext.data.Record} record
         */
        urlFor: function(record) {
            return '/' + record.owner + BQJSConfig['app_' + record.type + '_see'] + '/' + record.id;
        },

         /**
         * isCover
         * @author Jesse Sutherland
         * compares the photos to their parent album's cover and notifies the user if they are the same picture - used in album view
         * @param {integer} file
         * @return {boolean}
         */
        isCover: function(file) {
            return (BQ.content.TemplateMethods.getAlbumCover() == file) ? true : false;
        },

        /**
         * setCover
         * set new cover image id
         */
        setCover : function(id) {
            BQ.content.Template
        }
    };


}();

/**
 * BQ.content.Editor
 * A plugin that attaches handlers for edit, delete actions on a DataView Record
 */
BQ.content.Editor = function(){ }
BQ.content.Editor.prototype = {
    /**
     * @cfg {String} controller
     * the API script this component would use
     */
    controller: null,
    
    /**
     * @cfg {String} xtype
     * a registered Ext component type 
     */
    xtype: 'contentform',

    /**
     * @cfg {Object} actions
     * the actions that can be used with the API script
     */
    actions : {
        "edit" : "edit",
        "delete" : "delete",
        "load" : "load",
        "cover" : "setAsCover"
    },

    /**
     * @cfg {String} albumCoverCls
     * The css class to tag apply to album cover thumbs.
     */
    albumCoverCls : 'album-cover',

    /**
     * @cfg {String} coverImageId
     * The dom id of the album cover image
     */
    albumImageId : 'album_cover',
    /**
     * @cfg {string} coverThumb
     * The name of the thumb to use for albumImage, eg [thumb_preview, thumb_small, thumb_square, etc]
     */
    coverThumb : 'thumb_preview',

    /**
     * init
     * called automatically by Ext Plugin architecture
     * @param {Ext.DataView} view
     */
    init : function(panel) {
        // set url
        this.controller = panel.controller;

        var img = Ext.get(this.albumImageId);
        if (img) {
            img.on('load', function(){

                img.shift({
                    opacity: 1
                });
            });
        }
        // listen to clicks on view.  send all to this.onClick
        panel.on('toolclick', this.onClick, this);
    },

    // private.  returns Popup found in Ext.ComponentMgr or creates it
    getPopup : function() {

    /**
    * -- PATCH --
    * modifications Vincent Maillot vincent.maillot@cossette.com
    * date          09/08/05
    * 5930 : genere une nouvelle popup au lieu de recuperer l'existante
    **/
        if(Ext.getCmp('content_popup'))
        {
            var object = Ext.getCmp('content_popup').form;
            object.destroy();
        }

        var form = new BQ.content.Form({
            title: 'empty_str',
            width: 400,
            shadow: true,
            buttonAlign:'left',
            labelAlign: 'left'
         }) ;
        var popup = Ext.getCmp('content_popup') || null;
        return new RExt.form.Popup({
            id: 'content_popup',
            shadow:true,
            form:form
        });
    },

    /**
     * onClick
     * @param {DataView} v
     * @param {Number} index
     * @param {DomNode} node
     * @param {Element} btn
     * @param {Object} ev
     */
    onClick : function(v, index, node, btn, ev) {
        if (btn.hasClass('delete')) {
            this.onDelete(v, index, node, btn, ev);
        }
        else if (btn.hasClass('edit')) {
            this.onEdit(v, index, node, btn, ev);
        }
        else if (btn.hasClass('cover')) {
            this.onSetAsCover(v, index, node, btn, ev);
        }
    },

    /**
     * onEdit
     * Edit was selected
     * @param {Ext.DataView} view
     * @param {Number} index
     * @param {DomNode} node
     * @param {HTMLElement} btn
     * @param {Ext.EventObject} ev
     */
    onEdit : function(view, index, node, btn, ev) {
        // get a record from the store
        var rec = view.store.getAt(index);
        // ask the API for more details about this content
        App.request({
            url: this.controller + '/' + this.actions.load,
            params: {
                id: rec.data.id
            },
            method: 'GET',
            /**
             * success
             * we received more information from the server
             * @param {Object} res
             */
            success : function(res) {
                var popup = this.getPopup();
                //ev.stopEvent();
                var fpanel = popup.form;
                popup.show(btn);
                fpanel.showUpdate();
                fpanel.setKey(rec.data.id);
                fpanel.setValues(res.data);
                fpanel.on('actioncomplete', function(form, action) {
                    var data = action.result.data;
                    popup.hide();
                    var values = fpanel.form.getValues();
                    for (var k in data) {
                        rec.set(k, data[k]);
                    }
                    //rec.set('id', data.id);
                    rec.commit();
                });
            },
            scope: this
        });
    },

    /**
     * onDelete
     * delete some content
     * @param {Ext.DataView} view
     * @param {Number} index
     * @param {DomNode} node
     * @param {HTMLElement} btn
     * @param {Ext.EventObject} ev
     */
    onDelete : function(view, index, node, btn, ev) {
        // get the record to be deleted
        var rec = view.store.getAt(index);

        Ext.MessageBox.confirm(App.t('confirm'), App.t('delete') + ' ' + rec.data.title + '?', function(btn) {
            if (btn == 'yes') {
                App.showSpinner('phrase_please_wait');
                App.request({
                    url:this.controller + '/' + this.actions['delete'],
                    params:{
                        id: rec.data.id
                    },
                    method: 'GET',
                    /**
                     * success
                     * the server has responded that this content will be removed
                     * @param {Object} res
                     */
                    success:function(res) {
                        var resType = res.type;
                        view.store.remove(rec);
                        //check to see if this is the last record
                            if(view.store.getCount() == 0) {
                             view.refresh(); //so that we see the default no results text for an album
                            }
                        //
                        if (resType =="COVER_DELETED_SUCCESS" && res.data){//Do DOM CHANGE if the cover got deleted and a new cover exists
                            //the rest below is pureley cosmetic
                            if (view.store.getCount() >= 0) {
                                Ext.fly(view.getNode(view.store.find('id',res.data.id))).addClass('album-cover');
                            }
                            // change album cover image
                                var img = Ext.get("album_cover");//DOM element
                                img.shift({
                                    opacity:0.0,
                                    callback: function() {
                                        img.dom.src = res.data.thumb_preview;
                                    }
                                    });

                        }//end of changing DOM
                        App.hideSpinner();
                    },
                    failure: function(res) {
                        App.hideSpinner();
                    }
                });
            }

        },this);
    },

    /**
     * onSetCover 
     * User has decided to change the cover for their album
     * @param {Ext.DataView} view
     * @param {Number} index
     * @param {DomNode} node
     * @param {HTMLElement} btn
     * @param {Ext.EventObject} ev
     */
    onSetAsCover : function(view, index, node, btn, ev) {
        // get a record from the store
        var rec = view.store.getAt(index);

        App.request({
            url: this.controller + '/' + this.actions['cover'],
            method: 'POST',
            params: {
                id: rec.data.id
            },
            /**
             * success
             * it all worked out with the server and a new album cover has been assigned
             * @param {Object} res
             */
            success : function(res) {

                // set new cover image.  remove old cover image className; apply to new one.
                  if(Ext.select('div.album-cover').elements.length > 0){ //check and see if we even have a node with the class of cover
                        Ext.fly(view.getNode(view.store.findBy(function(r) {
                        return (BQ.content.TemplateMethods.isCover(r.data.id)) ? true : false; }))).removeClass(this.albumCoverCls);
                  };

                var newRecord = view.store.getAt(index);

                // OMG!  re-write this getAlbumCover method on BQ.content.TemplateMethods.  this function
                // is initially rendered in voir with smarty var {$album->file}.  This is tricky.
                BQ.content.TemplateMethods.getAlbumCover = function() {
                    return newRecord.data.id;
                }

                /**
                * Vincent Maillot vincent.maillot@cossette.com
                * 10/01/07
                * Ticket 6182: En édition il arrive que les saisons et les mots clés ne soient pas affichés 
                * => note 0030392 : si un album subit une duplication par BQAContent::setAsCover() on récupère le username
                * donc on en profite pour reloader la bonne / derniere version de l'album
                **/
                if(res.data.ownerUsername)
                {
                    window.location = res.data.id;
                }
                // sinon on ne fait que raffraichir la couverture
                else
                {
                
                    Ext.fly(view.getNode(index)).addClass(this.albumCoverCls);

                    // change album cover image
                    var img = Ext.get(this.albumImageId);
                    img.shift({
                        opacity:0.0,
                        callback: function() {
                            img.dom.src = rec.data[this.coverThumb];
                        },
                        scope: this
                    });
                }

            },
            failure : function(res) {

            },
            scope: this
        });
    }
};


/**
 * BQ.content.NoMapEditor
 * A plugin that attaches handlers for edit, delete actions on a DataView Record
 * but it doesnt display a map.
 */
BQ.content.NoMapEditor = function(){ }
BQ.content.NoMapEditor.prototype = {

    controller: null,
    actions : {
        "edit" : "edit",
        "delete" : "delete",
        "load" : "load",
        "cover" : "setAsCover"
    },

    /**
     * @cfg {String} albumCoverCls
     * The css class to tag apply to album cover thumbs.
     */
    albumCoverCls : 'album-cover',

    /**
     * @cfg {String} coverImageId
     * The dom id of the album cover image
     */
    albumImageId : 'album_cover',
    /**
     * @cfg {string} coverThumb
     * The name of the thumb to use for albumImage, eg [thumb_preview, thumb_small, thumb_square, etc]
     */
    coverThumb : 'thumb_preview',

    /**
     * init
     * called automatically by Ext Plugin architecture
     * @param {Ext.DataView} view
     */
    init : function(panel) {
        // set url
        this.controller = panel.controller;

        var img = Ext.get(this.albumImageId);
        if (img) {
            img.on('load', function(){

                img.shift({
                    opacity: 1
                });
            });
        }
        // listen to clicks on view.  send all to this.onClick
        panel.on('toolclick', this.onClick, this);
    },

    // private.  returns Popup found in Ext.ComponentMgr or creates it
    getPopup : function() {
        var popup = Ext.getCmp('content_popup') || null;
        return (popup) ? popup : new RExt.form.Popup({
            id: 'content_popup',
            shadow:true,
            form: new BQ.content.NoMapForm({
                title: '&nbsp;',
                width: 400,
                shadow: true,
                buttonAlign:'left',
                labelAlign: 'left'
            })
        });
    },

    /**
     * onClick
     * @param {DataView} v
     * @param {Number} index
     * @param {DomNode} node
     * @param {Element} btn
     * @param {Object} ev
     */
    onClick : function(v, index, node, btn, ev) {
        if (btn.hasClass('delete')) {
            this.onDelete(v, index, node, btn, ev);
        }
        else if (btn.hasClass('edit')) {
            this.onEdit(v, index, node, btn, ev);
        }
        else if (btn.hasClass('cover')) {
            this.onSetAsCover(v, index, node, btn, ev);
        }
    },

    onEdit : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);
        App.request({
            url: this.controller + '/' + this.actions.load,
            params: {
                id: rec.data.id
            },
            method: 'GET',
            success : function(res) {
                var popup = this.getPopup();
                //ev.stopEvent();
                var fpanel = popup.form;
                popup.show(btn);
                fpanel.showUpdate();
                fpanel.setKey(rec.data.id);
                fpanel.setValues(res.data);
                fpanel.on('actioncomplete', function(form, action) {
                    var data = action.result.data;
                    popup.hide();
                    var values = fpanel.form.getValues();
                    for (var k in data) {
                        rec.set(k, data[k]);
                    }
                    //rec.set('id', data.id);
                    rec.commit();
                });
            },
            scope: this
        });
    },

    onDelete : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);
        Ext.MessageBox.confirm(App.t('confirm'), App.t('delete') + ' ' + rec.data.title + '?', function(btn) {
            if (btn == 'yes') {
                App.showSpinner('phrase_please_wait');
                App.request({
                    url:this.controller + '/' + this.actions['delete'],
                    params:{
                        id: rec.data.id
                    },
                    method: 'GET',
                    success:function(res) {
                        view.store.remove(rec);
                        App.hideSpinner();

                    },
                    failure: function(res) {
                        App.hideSpinner();
                    }
                });
            }
        },this);
    },

    onSetAsCover : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);

        App.request({
            url: this.controller + '/' + this.actions['cover'],
            method: 'POST',
            params: {
                id: rec.data.id
            },
            success : function(res) {
                // set new cover image.  remove old cover image className; apply to new one.
                Ext.fly(view.getNode(view.store.findBy(function(r) {return (BQ.content.TemplateMethods.isCover(r.data.id)) ? true : false; }))).removeClass(this.albumCoverCls);

                var newRecord = view.store.getAt(index);

                // OMG!  re-write this getAlbumCover method on BQ.content.TemplateMethods.  this function
                // is initially rendered in voir with smarty var {$album->file}.  This is tricky.
                BQ.content.TemplateMethods.getAlbumCover = function() {
                    return newRecord.data.id;
                }

                Ext.fly(view.getNode(index)).addClass(this.albumCoverCls);

                // change album cover image
                var img = Ext.get(this.albumImageId);

                img.shift({
                    opacity:0.0,
                    callback: function() {
                        img.dom.src = rec.data[this.coverThumb];
                    },
                    scope: this
                });

            },
            failure : function(res) {
            },
            scope: this
        });
    }
};

/**
 * BQ.content.CarnetEditor
 * A plugin that attaches handlers for edit, delete actions on a DataView Record
 */
BQ.content.CarnetEditor = function(){ }
BQ.content.CarnetEditor.prototype = {
    controller: null,
    actions : {
        "edit" : "edit",
        "delete" : "delete",
        "load" : "load",
        "cover" : "setAsCover"
    },

    /**
     * @cfg {String} albumCoverCls
     * The css class to tag apply to album cover thumbs.
     */
    albumCoverCls : 'album-cover',

    /**
     * @cfg {String} coverImageId
     * The dom id of the album cover image
     */
    albumImageId : 'album_cover',
    /**
     * @cfg {string} coverThumb
     * The name of the thumb to use for albumImage, eg [thumb_preview, thumb_small, thumb_square, etc]
     */
    coverThumb : 'thumb_preview',

    /**
     * init
     * called automatically by Ext Plugin architecture
     * @param {Ext.DataView} view
     */
    init : function(panel) {
        this.controller = panel.controller;
        panel.on('toolclick', this.onClick, this);
    },

    // private.  returns Popup found in Ext.ComponentMgr or creates it
    getPopup : function() {
        var popup = Ext.getCmp('carnet_popup') || null;
        return new RExt.form.Popup({
            id: 'carnet_popup',
            form: new BQ.content.CarnetForm({
                title: '&nbsp;',
                width: 400,
                shadow: true,
                buttonAlign:'left',
                labelAlign: 'left'
            })
        });
    },

    /**
     * onClick
     * @param {DataView} v
     * @param {Number} index
     * @param {DomNode} node
     * @param {Element} btn
     * @param {Object} ev
     */
    onClick : function(v, index, node, btn, ev) {
        if (btn.hasClass('delete')) {
            this.onDelete(v, index, node, btn, ev);
        }
        else if (btn.hasClass('edit')) {
            this.onEdit(v, index, node, btn, ev);
        }
        else if (btn.hasClass('cover')) {
            this.onSetAsCover(v, index, node, btn, ev);
        }
    },

    onEdit : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);
        App.request({
            url: this.controller + '/' + this.actions.load,
            params: {
                id: rec.data.id
            },
            method: 'GET',
            success : function(res) {
                var popup = this.getPopup();
                //ev.stopEvent();
                var fpanel = popup.form;
                popup.show(btn);
                fpanel.showUpdate();
                fpanel.setKey(rec.data.id);
                fpanel.setValues(res.data);
                fpanel.on('actioncomplete', function(form, action) {
                    var data = action.result.data;
                    popup.hide();
                    var values = fpanel.form.getValues();
                    for (var k in data) {
                        rec.set(k, data[k]);
                    }
                    //rec.set('id', data.id);
                    rec.commit();
                });
            },
            scope: this
        });
    },

    onDelete : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);
        Ext.MessageBox.confirm(App.t('confirm'), App.t('delete') + ' ' + rec.data.title + '?', function(btn) {
            App.showSpinner('phrase_please_wait');
            if (btn == 'yes') {
                App.request({
                    url:this.controller + '/' + this.actions['delete'],
                    params:{
                        id: rec.data.id
                    },
                    method: 'GET',
                    success:function(res) {
                        view.store.remove(rec);
                        App.hideSpinner();
                    },
                    failure: function(res) {
                        App.hideSpinner();
                    }
                });
            }
        },this);
    },

    onSetAsCover : function(view, index, node, btn, ev) {
        var rec = view.store.getAt(index);

        App.request({
            url: this.controller + '/' + this.actions['cover'],
            method: 'POST',
            params: {
                id: rec.data.id
            },
            success : function(res) {
                // set new cover image.  remove old cover image className; apply to new one.
                Ext.fly(view.getNode(view.store.findBy(function(r) {return (BQ.content.TemplateMethods.isCover(r.data.id)) ? true : false; }))).removeClass(this.albumCoverCls);

                var newRecord = view.store.getAt(index);

                // OMG!  re-write this getAlbumCover method on BQ.content.TemplateMethods.  this function
                // is initially rendered in voir with smarty var {$album->file}.  This is tricky.
                BQ.content.TemplateMethods.getAlbumCover = function() {
                    return newRecord.data.id;
                }

                Ext.fly(view.getNode(index)).addClass(this.albumCoverCls);

                // change album cover image
                var img = Ext.get(this.albumImageId);

                img.shift({
                    opacity:0.0,
                    callback: function() {
                        img.dom.src = rec.data[this.coverThumb];
                    },
                    scope: this
                });

            },
            failure : function(res) {
            },
            scope: this
        });
    }
};


/**
 * BQ.content.Voter
 * A content-voting plugin.  listens to click on voting tool.  sends Ajax request to server
 * @author Chris Scott
 */
BQ.content.Voter = function() {}
BQ.content.Voter.prototype = {
    controller: null,

    init : function(panel) {
        // set url
        this.controller = panel.controller;
        this.actions = {
            "vote" : "vote"
        }

        // listen to toolclick
        panel.on('toolclick', function(view, index, btn, ev) {
            if (btn.hasClass('vote')) {
                this.onVote(v, index, btn, ev);
            }
        });
    },

    /**
     * onVote
     * @param {Ext.DataView} v
     * @param {Number} index
     * @param {Element} node
     * @param {EventObject} ev
     */
    onVote : function(v, index, node, ev) {
        App.setAlert(App.STATUS_NOTICE, 'onVote');

    }
}

/**
 * BQ.content.DataView
 * an RExt.DataView, which is a panel that has a DataView as it's raison d'etre
 * the key to these components is their getView and template handling functionality
 */
BQ.content.DataView = Ext.extend(RExt.DataView, {
    /**
     * @cfg {Boolean} frame
     * this panel will use the frame styling
     */
    frame: true,
    
    /**
     * @cfg {Boolean} border
     * this panel will not present a border
     */
    border: false,
    
    /**
     * @cfg {String} title
     * this panel has a space for a title
     */
    title:App.t('empty_str'),
    
    /**
     * 
     */
    autoHeight: true,
    stateful:false,
    afterPageText: 'Bq.content.DataView.afterPageText {1}',

    // private
    _defaultViewConfig : {
        autoWidth: true,
        autoHeight:true, //must be true for IE 7
        multiSelect: true,
        deleted: [],
        autoScroll: true,
        overClass:'x-grid3-row-over',
        itemSelector:'div.x-grid3-row',
        selectedClass: 'x-grid3-row-selected',
        emptyText: '<h1 class=\'no_results\'>' + App.t('no_results') + '</h1>'
    },

    recordType: BQ.data.Content,

    /**
     * @cfg {Number} pageSize
     */
    pageSize: 10,

    /**
     * @cfg {String} templateId [content_template]
     * id of XTemplate to load from RExt.TemplateMgr
     */
    templateId : 'content_template',

    /**
     * @cfg {String} url [/json/BQAContent/search]
     * The data-url to the mother-ship
     */
    controller : '/json/BQAContent',
    
    /**
     * @cfg {Object} actions
     * the various actions for the API script
     */
    actions: {
        search : 'search'
    },

    /**
     * @cfg {String} sort
     * a private vble for keeping track of the sort ordering
     */
    sort: null,

    initComponent : function() {

        // if Media type is set to tous, make it emtpy-string.  server is trained to figure this out.
        if (this.baseParams.types == 'tous') {
            this.baseParams.types = '';
        }

        this.addClass('bq-list');

        // super
        BQ.content.DataView.superclass.initComponent.call(this);
    },

    /**
     * buildTopToolbar
     * provide a paging toolbar at the top of the Dataview
     * @param {Object} store
     */
    buildTopToolbar : function(store) {
       /**
        * create an object defining all the particulars of the status bar we want to create
        */
        var cfg = {
            cls:"clean",
            store: store,
            pageSize: this.pageSize,
            displayInfo: true,
            displayMsg: BQJSLabels.words_results + " {0} - {1} " + BQJSLabels.words_of + " {2}",
            afterPageText: BQJSLabels.words_of + " {0}",
            emptyMsg : App.t('toolbar_no_results'),
            statusAlign: 'right'
        };
        // PATCH Vincent Maillot vincent.maillot@cossette.com
        // 5434 - Retrait des filtres de Mes Photos
        if (!this.baseParams.heart) {
            if(this.baseParams.types == 'video')
            {
                cfg.items = [
                    {text: App.t('most_recent'), value: '_time_created', enableToggle: true, params: {heart: 0, sort: '_time_created DESC'}, toggleGroup: 'sort-' + this.id, pressed: true, toggleHandler: this.onSort, scope: this},
                    {text: App.t('top_rated'), value: 'rating', enableToggle: true, params: {heart: 1, sort: 'rating DESC'}, toggleGroup: 'sort-' + this.id, toggleHandler: this.onSort, scope: this}
                ];
            }
            else
            {
                cfg.items = [
                    {text: App.t('empty_str'), enableToggle: false, scope: this}
                ];
            }
        }
        
        /**
         * toolbar will in fact be an Ext.StatusBar element, which is slightly different from a conventional toolbar
         */
        var tbar = new Ext.StatusBar(cfg);

        /**
         * bind the toolbar to the store, hooking toolbar clicks directly to the store
         * @param {Object} store
         */
        tbar.bind = function(store) {
            // manually update paging info on store load.  copied from PagingToolbar
            store.on('load', function(store, rs, options) {
                var count = rs.length;
                var msg = (count == 0) ?
                    // VM 10/01/13 4748: Page sans résultat a une présentation incorrecte => on retire le titre "Aucun resultat"
                    '' :
                    String.format(
                        tbar.displayMsg,
                        options.params.start+1, options.params.start+count, store.getTotalCount()
                    );
                tbar.setStatus(msg);
            },this);
        }
        tbar.bind(store);


        return tbar;
    },

    /**
     * buildBottomToolbar
     * a normal Ext paging toolbar for the Dataview
     * @param {Object} store
     */
    buildBottomToolbar : function(store) {
        var bbar = new Ext.PagingToolbar({
            cls:"clean",
            store: store,
            pageSize: this.pageSize,
            displayInfo: true,
            emptyMsg : App.t('toolbar_no_results'),
            displayMsg: BQJSLabels.words_results + " {0} - {1} " + BQJSLabels.words_of + " {2}",
            afterPageText: BQJSLabels.words_of + " {0}",
            listeners: {
                render: function (l) {
                    this.loading.hide();
                }
            }
        });
        
        /**
        * Vincent Maillot vincent.maillot@cossette.com
        * 10/01/12
        * Ticket 4748: Page sans résultat a une présentation incorrecte 
        * => on cache la bottomToolbar s'il n'y a pas de resultat
        **/
        store.on('load', function(store, rs, options) {
            var count = rs.length;
            if(count == 0) {
                bbar.hide();
            }
        },this);
        
        return bbar;
        
    },

    /**
     * onSort
     * The status bar has toggle handlers for most recent and top rated, processed here 
     * @param {Ext.Button} btn
     * @param {String} state
     */
    onSort : function(btn, state) {
        if (btn.pressed == false) {   // <-- run away if this button is already pressed.
            return false;
        }
        var store = this.getView().store;
        store.baseParams = Ext.apply(store.baseParams, btn.params);
        this.load(1);
    }
});
Ext.reg('bq-dataview', BQ.content.DataView);

/**
 * BQ.content.Search
 * @author Chris Scott
 */
BQ.content.Search = Ext.extend(Ext.TabPanel, {

    /**
     * @cfg {Object} baseParams
     * baseParams to relay to contained DataView
     */
    baseParams: {},

    bodyStyle: 'background-color: transparent;',

    /**
     * @cfg {String} filter [tous]
     * specifies the content-type to display [tous, video, photo, carnet]
     */
    filter : null,

    /**
     * @cfg {String/Element ID} syncSearchField [false]
     * the element id of main search box to synchronize DataView's search field with.
     */
    syncSearchField : false,

    /**
     * @cfg {Boolean} layoutOnTabChange
     * panel will auto layout between clicks on tabs
     */
    layoutOnTabChange: true,
    
    /**
     * @cfg {Boolean} border
     * panel does not draw a border
     */
    border: false,
    
    /**
     * @cfg {Boolean} plain
     * render tabs with a background container image
     */
    plain: true,
    
    /**
     * @cfg {Number} activeTab
     * default the first tab as active
     */
    activeTab: 0,

    /**
     * @cfg {Number} minTabWidth
     * tabs will be 140 pixels wide
     */    
    minTabWidth: 140,
    
    /**
     * @cfg {Boolean} resizeTabs
     * we don't want the tab sizes changing
     */
    resizeTabs: false,
    
    /**
     * @cfg {Number} width
     * the panel will be 620 pixels wide
     */
    width:620,
    
    /**
     * @cfg {String} cls
     * container element gets this class added to it
     */
    cls:'no-spacer drop_curl',

    // private.  ptr -> contained RExt.DataView
    view : null,

    // private
    initComponent : function() {

        // init heart param to boolean.
        this.baseParams.heart = (parseInt(this.baseParams.heart)) ? 1 : 0;

        // set activeTab based upon incoming baseParams
        for (var n=0,len=this.items.length;n<len;n++) {
            if (this.items[n].params.types == this.baseParams.types) { this.activeTab = n; }
            
            Ext.apply(this.items[n], {
                templateId: this.templateId,
                listeners: {activate: this.onActivate, deactivate: this.onDeactivate, scope: this},
                xtype: 'bq-dataview',
                autoLoad: false,
                baseParams: {}
            });
            Ext.apply(this.items[n].baseParams, this.items[n].params, this.baseParams);
        }
        if (this.baseParams.types == '') { this.activeTab = 0; }

        // super-duper
        BQ.content.Search.superclass.initComponent.call(this);

    },

    /**
     * getView
     * @param {Object} params, baseParams to configure RExt.DataView.  store auto-loads when instantiated.
     */
    getView : function() {
        return this.view;
    },

    /**
     * onActivate
     * @param {Ext.Panel} p
     * move the DataView onto selected tab
     */
    onActivate : function(p) {
        var store = p.getView().store;
        if (store.lastOptions == null) {
            p.load(1);
        }
    },

    /**
     * onDeactivate
     * fires when a tab loses focus, nothing going on here 
     * @param {Ext.Panel} p
     */
    onDeactivate : function(p) {
        return false;
    },

    // private doLoad
    doLoad : function(panel) {
        var search = Ext.getCmp('search_field');
        if (search) {
            if (this.syncSearchField !== false) {
                // keep some other search box synchronized with DataView's SearchField in toolbar.
                var appSearch = Ext.get(this.syncSearchField);
                if (appSearch) {
                    this.getView().store.on('load', function(store, rs, options){
                        if (typeof(options.params.query) != 'undefined') {
                            appSearch.dom.value = options.params.query;
                        }
                    });
                }
                else {

                }
            }
        }
        if (this.baseParams.query && search) {
            search.execute();
        }
        else {
            BQ.content.Search.superclass.doLoad.call(this, panel);
        }
    },

    /**
     * applyFilter
     * @param {Object} filter, contains baseParams for store's load
     */
    applyFilter : function(filter) {
        this.items.each(function(i) {   // <-- nullify last-options to signal a reload is required
            var store = i.getView().store;
            store.lastOptions = null;
            Ext.apply(store.baseParams, filter); // <-- apply filter to store's baseParams
        });
        var first = this.items.first();
        if (this.getActiveTab().id == first.id) {
            first.load(1);  // <-- if first tab is currently activated, force it to load.
        }
        else {              // <-- otherwise, just activate it -- the onActivate handler will handle to loading for us.
            this.activate(this.items.first());
        }
    }
});

/**
 * BQ.album.RegionField
 * Regions presented using Ext.ux.BoxSelect
 * This is one sweet little component
 * http://extjs.com/forum/showthread.php?t=33794
 * @author Chris SCott
 */
BQ.content.RegionField = Ext.extend(Ext.ux.BoxSelect, {
    name: 'regions',
    mode: 'local',
    ctCls: 'region-field',
    fieldLabel: 'Region',
    forceSelection: true,
    allowDuplicate: false,
    shadow: false,
    displayField: 'name',
    valueField: 'id',
    hideTrigger: false,
    anchor: '95%',
    emptyText: App.t('phrase_choose_regions'),
    hiddenName: 'regions[]',

    initComponent : function() {

        this.store = new Ext.data.SimpleStore({
            fields: [
                {name: 'id', mapping: 'id'},
                {name: 'name', mapping: 'name'}
            ],
            data: BQ.content.Util.getRegions()
        });

        BQ.content.RegionField.superclass.initComponent.call(this);
    }

});
Ext.reg('regionfield', BQ.content.RegionField);

/**
 * BQ.content.CategoryField
 * Categories presented using Ext.ux.BoxSelect
 * This is one sweet little component
 * http://extjs.com/forum/showthread.php?t=33794
 * @author Chris Scott
 */
BQ.content.CategoryField = Ext.extend(Ext.ux.BoxSelect, {
    name: 'categories',
    mode: 'local',
    ctCls: 'region-field',
    fieldLabel: 'Categories',
    forceSelection: true,
    shadow: false,
    displayField: 'name',
    valueField: 'id',
    hideTrigger: false,
    anchor: '95%',
    allowDuplicate: false,
    emptyText: App.t('phrase_choose_categories'),
    hiddenName: 'categories[]',

    initComponent : function() {
        this.store = new Ext.data.SimpleStore({
            fields: [
                {name: 'id', mapping: 'id'},
                {name: 'name', mapping: 'name'}
            ],
            data: BQ.content.Util.getCategories()
        });

        BQ.content.CategoryField.superclass.initComponent.call(this);
    }
});
Ext.reg('categoryfield', BQ.content.CategoryField);

/**
 * BQ.content.SeasonsField
 * extends a CheckboxGroup element
 */
BQ.content.SeasonsField = Ext.extend(Ext.form.CheckboxGroup, {
    /**
     * @cfg {Boolean} vertical
     * arrange the season checkboxes in a vertical alignment
     */
    vertical : true,
    
    /**
     * @cfg {Number} columns
     * split the number of checkboxes into 2 columns
     */
    columns : 2,
    
    /**
     * @cfg {String} bodyStyle
     * make sure the panel body has no padding
     */
    bodyStyle: 'padding: 0',
    
    /** 
     * @cfg {String} name
     * we call these form elements seasons
     */
    name: 'seasons',
    
    /**
     * @cfg {String} id
     * explicit id for this component
     */
    id: 'seasons',
    
    /**
     * @cfg {Boolean} border
     * this panel will have no border
     */
    border: false,

    initComponent : function() {
        
        /**
         * call the helper method to get the seasons
         */
        var rs = BQ.content.Util.getSeasons();
        this.items = [];
        for (var n=0,len=rs.length;n<len;n++) {
            this.items.push({xtype: 'checkbox', id: 'season_' + rs[n].id, boxLabel: rs[n].name, name: 'seasons[]', inputValue: rs[n].id});
        }
        // super
        BQ.content.SeasonsField.superclass.initComponent.call(this);
    },

    /**
     * gather up all the checked seasons and provide a comma-delimited string
     */
    getValue : function() {
        vals = [];
        if (this.items) {
            this.items.each(function(i){
                if (i.getValue()) {
                    vals.push(parseInt(i.getRawValue()));
                }
            });
        }
        return vals.join(',');
    },

    /**
     * setValue
     * given some values, set the checkboxes up
     * @param {Object} values
     */
    setValue : function(values) {
            values = values || [];
            for (var n = 0, len = values.length; n < len; n++) {
                var item = this.items.find(function(i){
                    return (i.getRawValue() == values[n]) ? true : false;
                }, this);
                if (item) {
                    item.setValue(true);
                }
            }
    }
});
Ext.reg('seasonsfield', BQ.content.SeasonsField);


/**
 * BQ.content.Form
 * extends an RExt.sys.Form providing save/update buttons and easy access to form actions
 * @author Chris Scott
 */

BQ.content.Form = Ext.extend(RExt.sys.Form, {
    /**
     * @cfg {String} controller
     * the API script this component can call
     */
    controller: '/json/BQAContent',
    
    /**
     * @cfg {Object} actions
     * the actions this script can send to the API call
     */
    actions: {
        update: 'edit'
    },
    
    /**
     * @cfg {Boolean} plain
     * this tab panel will not provide any background image for the Tabs
     */
    plain: false,
    
    /** 
     * @cfg {Number} labelWidth
     * labels occupy 70 pixels
     */
    labelWidth: 70,
    
    /**
     * @cfg {Boolean} frame
     * do not provide all the nice styles to the form
     */
    frame: false,
    
    /**
     * cfg {Boolean} border
     * do not draw a nice border around this panel 
     */
    border:false,
    
    /**
     * @cfg {String} labelAlign
     * right-align labels
     */
    labelAlign: 'right',
    
    /**
     * @cfg {Boolean} shadow
     * do not provide any kind of drop shadow
     */
    shadow: false,
    
    /**
     * @cfg {Boolean} autoFocus
     * put the focus and cursor in the first text field of the form when rendered
     */
    autoFocus: false,
    
    /**
     * @cfg {Boolean} showBtnIcons
     * do not show any icons on the form buttons
     */
    showBtnIcons:false,

    initComponent : function() {
        /**
         * call the build method to provide the form elements needed
         */
        this.items = this.build();
        
        /**
         * onShow
         * when the form is shown we make sure there is a tool tip for it
         */
        /*
        * Vincent Maillot vincent.maillot@cossette.com
        * ticket 5930
        * utilisé lorsqu'on clique sur le bouton Edition dans la fenetre de moderation
        * voir : ModerationManager.js - BQ.moderation.ContentViewer::onEdit()
        */
        this.on('show', function() {
            new Ext.ToolTip({
                target: 'tags_field',
                html: App.t('tags_validation')
            });
            this.items.first().setActiveTab(0);
        },this);
        BQ.content.Form.superclass.initComponent.call(this);
        
        /**
         * tabpanel is the first item of the form, so if we want to mess with the form buttons, we can do that here.
         * BQ requested that we not do this, even though it tends to fix a problem with IE and iframe elements
         * So... at the moment, this code does nothing...
         */
        var tabPanel = this.items.first();

        if (tabPanel instanceof Ext.TabPanel) {
            
            // Ticket : 5930
            // Vincent Maillot vincent.maillot@cossette.com
            // Actions sur les Boutons "Propriétés" et "Carte" dans la fenetre de moderation
            // => click sur le bouton Carte
            tabPanel.items.last().on('activate', function (p) {
                for(var i = 0, len = this.buttons.length; i < len; i++) {
                    switch (this.buttons[i].id) {
                        case this.id + '_btn_update':
                        case this.id + '_btn_cancel':
                            //this.buttons[i].hide();
                    }
                }
            }, this);

            // => click sur le bouton Propriétés
            tabPanel.items.first().on('activate', function (p) {
                for(var i = 0, len = this.buttons.length; i < len; i++) {
                    switch (this.buttons[i].id) {
                        case this.id + '_btn_update':
                        case this.id + '_btn_cancel':
                            this.buttons[i].show();
                    }        
                }
            }, this);
            
            
        }
    },

    // implement REXt.sys.Form#getParams to return lat / lng of Map from url
    getParams : function() {
        // do regexp on window url to find lat/lng
        var m = window.location.hash.match(/lat=(.*)lng=(.*)/);
        if (m) {
            var coords =  {
                latitude: (!isNaN(m[1])) ? m[1] : 0,
                longitude: (!isNaN(m[2])) ? m[2] : 0
            };
            return coords;
        }
    },

    /**
     * setValues
     * override setValues to perform meticulous value-setting of regions, categories, seasons
     * @param {Object} data
     */
    setValues : function(data) {
        // have to be careful setting regions, categories and seasons.
        data['regions[]']       = [];
        data['categories[]']    = [];
        data[this.id + '_seasons']         = [];
        if (typeof(data._regions) == 'undefined') {
            data._regions = [];
        }
        if (typeof(data._categories) == 'undefined') {
            data._categories = [];
        }
        if (typeof(data._seasons) == 'undefined') {
            data._seasons = [];
        }
        for (var n=0,len=data._regions.length;n<len;n++) {
            data['regions[]'].push(data._regions[n].id);
        }
        for (var n=0,len=data._categories.length;n<len;n++) {
            data['categories[]'].push(data._categories[n].id);
        }
        for (var n=0,len=data._seasons.length;n<len;n++) {
            data[this.id + '_seasons'].push(data._seasons[n].id);
        }

        // init Map with contentId
        var map = this.getMap();
        if (map) {
            map.setContentId(data.id);
        }
        // superclasse
        BQ.content.Form.superclass.setValues.call(this, data);

    },

    /**
     * build
     * create a nice form with all the elements required for this form
     */
    build : function() {
        var sdata = BQ.content.Util.getSeasons();
        var seasons = [];

        for (var n=0,len=sdata.length;n<len;n++) {
            seasons.push({xtype: 'checkbox', id: 'season_' + sdata[n].id, boxLabel: sdata[n].name, name: 'seasons[]', inputValue: sdata[n].id});
        }
        var items = [
            {xtype: 'hidden', name: 'id', anchor: '94%', allowBlank: false},
            {xtype: 'textfield', name: 'title', fieldLabel: App.t('title'), anchor: '94%', allowBlank: false, autoCreate: {tag: "input", type: "text", autocomplete: "off", maxlength: "30"}, emptyText: App.t('phrase_empty_text_title')},
            {xtype: 'textarea', name: 'description', fieldLabel: 'Description', anchor: '94%', maxLength: 1024, height:'90px'},
            {xtype: 'seasonsfield', id: this.id + '_seasons', name: this.id + '_seasons', fieldLabel: App.t('album_add_seasons'), anchor: '94%'},
            {xtype: 'regionfield', fieldLabel: App.t('album_add_regions')},
            {xtype: 'categoryfield', fieldLabel: App.t('album_add_categories')},
            {xtype: 'textfield', fieldLabel: App.t('album_add_tags'), name: 'tags', anchor: '94%', allowBlank: true, id:'tags_field'}
        ];

        var d = new Date();
        
        /**
         * create the tabpanel. the panels inside the tabpanel will be the editing form (properties) and the korem map
         */
        return new Ext.TabPanel({
            id: this.id + '_tab_panel',
            activeTab: 0,
            plain: this.plain,
            border: true,
            items: [{
                xtype: 'panel',
                autoHeight: true,
                layout: 'form',
                title: App.t('properties'),
                items: items,
                bodyStyle: 'padding: 10px'
            }, {
                title: App.t('map'),
                url: BQJSConfig.korem_gateway + 'secondaryEditableMap.do?lang=' + BQJSConfig.current_lang + "&refresh="+d.getTime(),
                xtype: 'contentmap',
                height: 400,
                width:400 
            }]
        });
    },

    /**
     * make sure the properties and the map get reset if this component processes a reset
     */
    reset : function() {
        var map = this.getMap();
        if (map) 
        {        
            map.reset(); 
        }
        BQ.content.Form.superclass.reset.call(this);
    },

    /**
     * getMap
     * @return {BQ.content.Map}
     */
    getMap : function() {
        var rs = this.findBy(function(p) {
            return (p.xtype == 'contentmap') ? true : false;
        });
        return (rs.length > 0) ? rs[0] : null;
    }
});
Ext.reg('contentform', BQ.content.Form);


/**
 * BQ.content.NoMapForm
 * A meta properties editing component that does NOT provide a Korem map. Some content has no map right...
 * @author Pat
 */

BQ.content.NoMapForm = Ext.extend(RExt.sys.Form, {
    /**
     * @cfg {String} controlller
     * the API script called by this component
     */
    controller: '/json/BQAContent',
    
    /**
     * @cfg {Object} actions
     * the actions the API script will accept
     */
    actions: {
        update: 'edit'
    },
    
    /**
     * cfg {Boolean} plain
     * tab will not use a background container image
     */
    plain: false,
    
    /**
     * @cfg {Number} labelWidth
     * labels will only occupy 70 pixels
     */
    labelWidth: 70,
    
    /**
     * @cfg {Boolean} frame
     * do not use fancy form styles on this panel 
     */
    frame: false,
    
    /**
     * @cfg {Boolean} border
     * do not draw a nice border around this panel
     */
    border:false,
    
    /**
     * @cfg {String} labelAlign
     * align labels to the right
     */
    labelAlign: 'right',
    
    /**
     * @cfg {Boolean} don't use any form shadow elements
     */
    shadow: false,
    
    /**
     * @cfg {Boolean} autoFocus
     * do not focus on the first element of this form
     */
    autoFocus: false,
    
    /**
     * @cfg {Boolean} showBtnIcons
     * do not show any icons on the form buttons
     */
    showBtnIcons:false,
    
    initComponent : function() {
        /**
         * call the build method to provide all the elements for this form
         */
        this.items = this.build();
        BQ.content.NoMapForm.superclass.initComponent.call(this);
        
        /**
         * when this form shows itself, make sure it has a tooltip
         */
         this.on('show', function() {//adding tooltip for tags_field
            new Ext.ToolTip({
            target: 'tags_field',
            html: App.t('tags_validation')
            });
        });
    },

    /**
     * setValues
     * override setValues to perform meticulous value-setting of regions, categories, seasons
     * @param {Object} data
     */
    setValues : function(data) {
        // have to be careful setting regions, categories and seasons.
        data['regions[]']       = [];
        data['categories[]']    = [];
        data[this.id + '_seasons']         = [];
        if (typeof(data._regions) == 'undefined') {
            data._regions = [];
        }
        if (typeof(data._categories) == 'undefined') {
            data._categories = [];
        }
        if (typeof(data._seasons) == 'undefined') {
            data._seasons = [];
        }
        for (var n=0,len=data._regions.length;n<len;n++) {
            data['regions[]'].push(data._regions[n].id);
        }
        for (var n=0,len=data._categories.length;n<len;n++) {
            data['categories[]'].push(data._categories[n].id);
        }
        for (var n=0,len=data._seasons.length;n<len;n++) {
            data[this.id + '_seasons'].push(data._seasons[n].id);
        }

        // superduper
        BQ.content.NoMapForm.superclass.setValues.call(this, data);

    },

    /**
     * build
     * provide a regular Ext Panel and not a tab panel for this type of form.
     */
    build : function() {
        var sdata = BQ.content.Util.getSeasons();
        var seasons = [];

        for (var n=0,len=sdata.length;n<len;n++) {
            seasons.push({xtype: 'checkbox', id: 'season_' + sdata[n].id, boxLabel: sdata[n].name, name: 'seasons[]', inputValue: sdata[n].id});
        }
        var items = [
            {xtype: 'hidden', name: 'id', anchor: '94%', allowBlank: false},
            {xtype: 'textfield', name: 'title', fieldLabel: App.t('title'), anchor: '94%', allowBlank: false, autoCreate: {tag: "input", type: "text", autocomplete: "off", maxlength: "30"}, emptyText: App.t('phrase_empty_text_title')},
            {xtype: 'textarea', name: 'description', fieldLabel: 'Description', anchor: '94%', maxLength: 1024, height:'90px'},
            {xtype: 'seasonsfield', id: this.id + '_seasons', name: this.id + '_seasons', fieldLabel: App.t('album_add_seasons'), anchor: '94%'},
            {xtype: 'regionfield', fieldLabel: App.t('album_add_regions')},
            {xtype: 'categoryfield', fieldLabel: App.t('album_add_categories')},
            {xtype: 'textfield', fieldLabel: App.t('album_add_tags'), name: 'tags', anchor: '94%', allowBlank: true, id:'tags_field'}
        ];

        /**
         * could probably not return TWO panels here. One should do nicely
         */
        return new Ext.Panel({
            id: this.id + '_panel',
            border: true,
            items: [{
                xtype: 'panel',
                autoHeight: true,
                layout: 'form',
                header: false,
                title: App.t('properties'),
                items: items,
                bodyStyle: 'padding: 10px'
            }]
        });
    }
});



Ext.reg('nomapform', BQ.content.NoMapForm);

/**
 * BQ.content.CarnetForm
 * - allow a user to change the meta data associated with a carnet
 * including the option to change the cover photo...
 * @author D. Lazar
 */

BQ.content.CarnetForm = Ext.extend(RExt.sys.Form, {
    controller: '/json/BQAContent',
    actions: {
        update: 'edit'
    },
    plain: false,
    labelWidth: 100,
    frame: false,
    border:false,
    labelAlign: 'right',
    shadow: false,
    autoFocus: false,
    showBtnIcons: false,
    data: null,
    initComponent : function() {
        this.items = this.build();
        BQ.content.CarnetForm.superclass.initComponent.call(this);
         this.on('show', function() {//adding tooltip for tags_field
            new Ext.ToolTip({
            target: 'tags_field',
            html: App.t('tags_validation')
            });
        });
    },

    /**
     * setValues
     * override setValues to perform meticulous value-setting of regions, categories, seasons
     * in case we have previously set a carnet cover, we should also take care of that.
     * @param {Object} data
     */
    setValues : function(data) {
        //console.log('data for carnet form is ', data);
        // have to be careful setting regions, categories and seasons.
        data['regions[]']       = [];
        data['categories[]']    = [];
        data[this.id + '_seasons']         = [];
        if (typeof(data._regions) == 'undefined') {
            data._regions = [];
        }
        if (typeof(data._categories) == 'undefined') {
            data._categories = [];
        }
        if (typeof(data._seasons) == 'undefined') {
            data._seasons = [];
        }
        for (var n=0,len=data._regions.length;n<len;n++) {
            data['regions[]'].push(data._regions[n].id);
        }
        for (var n=0,len=data._categories.length;n<len;n++) {
            data['categories[]'].push(data._categories[n].id);
        }
        for (var n=0,len=data._seasons.length;n<len;n++) {
            data[this.id + '_seasons'].push(data._seasons[n].id);
        }

        if (data.thumb_square) {
            // create a record for the UserPhoto dropdown
            var combo = this.getCombo();
            combo.store.add(new combo.store.recordType({
                id: data.file,
                title: data.thumb_square.split('/').pop()
            }));
            combo.setValue(data.file);
        }
        // superduper
        BQ.content.CarnetForm.superclass.setValues.call(this, data);
    },

    build : function() {

        var items = [
            {xtype: 'hidden', name: 'id', anchor: '94%', allowBlank: false},
            {xtype: 'textfield', name: 'title', fieldLabel: App.t('story_title'), anchor: '94%', allowBlank: false, autoCreate: {tag: "input", type: "text", autocomplete: "off", maxlength: "30"}, emptyText: App.t('phrase_empty_text_title')},
            {xtype: 'textarea', name: 'description', fieldLabel: App.t('story_description'), anchor: '94%', maxLength: 1024},
            {xtype: 'seasonsfield', id: this.id + '_seasons', name: this.id + '_seasons', fieldLabel: App.t('story_seasons'), anchor: '94%'},
            {xtype: 'regionfield', fieldLabel: App.t('story_regions')},
            {xtype: 'categoryfield', fieldLabel: App.t('story_categories')},
            {xtype: 'textfield', fieldLabel: App.t('story_tags'), name: 'tags', anchor: '94%', allowBlank: true, id:'tags_field'},
            {xtype: 'userphotos', fieldLabel: App.t('story_cover_photo')}
        ];

        return new Ext.Panel({
            id: this.id + '_panel',
            border: true,
            autoHeight:true,
            layout: 'form',
            bodyStyle: 'padding: 10px',
            items: items
        });
    },

    /**
     * getPanel
     * returns the first panel of this component, the one holding all the form fields
     */
    getPanel : function () {
        return this.items.first();
    },

    /**
     * getCombo
     * searches through the items collection of the panel for the one matching the user photo combo
     */
    getCombo : function () {
        return this.getPanel().items.find(function(p) {
            return (p.xtype == 'userphotos') ? true : false;
        });

        /*var panel = this.items.first();
        var items = panel.items;
        var c = {};
        items.each(function (i){
            if ( i.xtype == 'userphotos') {
                c = i;
            }
        });
        return c;*/
    }

});
Ext.reg('carnetform', BQ.content.CarnetForm);


/**
 * BQ.content.BasicForm
 * An Ext.form.FormPanel for collecting meta information for Albums
 * @author Chris Scott
 */

BQ.content.BasicForm = Ext.extend(Ext.form.FormPanel, {
    /**
     * @cfg {String} controller
     * the API script this component calls
     */
    controller: '/json/BQAContent',
    
    /**
     * @cfg {Object} actions
     * the actions the API script will respond to 
     */
    actions: {
        update: 'edit'
    },
    
    /**
     * @cfg {Boolean} plain
     * the tabs will not use a background image in their container
     */
    plain: false,
    
    /**
     * @cfg {Number} labelWidth
     * labels will occupy 70 pixels
     */
    labelWidth: 70,
    
    /**
     * @cfg {Boolean} frame
     * do not use the nice form styling elements for this panel
     */
    frame: false,
    
    /**
     * @cfg {Boolean} border
     * do not draw a nice border around this panel
     */
    border: false,
    
    /**
     * @cfg {String} labelAlign
     * align labels to the right on the form
     */
    labelAlign: 'right',
    
    /**
     * @cfg {Boolean} shadow
     * do not use any shadow elements around this panel 
     */
    shadow: false,
    
    /**
     * @cfg {Boolean} autoFocus
     * not really a property of Ext.form.FormPanels... but hey...
     */
    autoFocus: false,
    
    /**
     * @cfg {String} url
     * an URL that is probably not used for anything here
     */
    url: null,

    initComponent : function() {
        /**
         * call Bob the builder
         */

        this.items = this.build();
        /**
         * not using RExt.sys.Form means we have to provide the buttons
         */
        this.buttons = [{
            text: App.t('update'),
            handler: this.onUpdate,
            scope: this
        }, {
            text: App.t('cancel'),
            handler: this.onCancel,
            scope: this

        }];

        if (this.standardSubmit) {
           this.on('render', function() {
               this.form.el.dom.setAttribute('action', this.url);
           }, this);
        }

        BQ.content.BasicForm.superclass.initComponent.call(this);
         this.on('afterlayout', function() {//adding tooltip for tags_field
            new Ext.ToolTip({
            target: 'tags_field',
            html: App.t('tags_validation')
            });
        });
    },

    /**
     * onUpdate
     * if the update button was pressed, submit the form if it was valid
     */
    onUpdate : function() {
        if (!this.form.isValid()) {
            return false;
        }
        this.form.submit();
    },

    /**
     * onCancel
     * at the moment this function does not do too much
     */
    onCancel : function() {
        //window.location = ""
    },




    /**
     * setValues
     * override setValues to perform meticulous value-setting of regions, categories, seasons
     * @param {Object} data
     */
    setValues : function(data) {

        // have to be careful setting regions, categories and seasons.
        data['regions[]']       = [];
        data['categories[]']    = [];
        data[this.id + '_seasons']         = [];
        if (typeof(data._regions) == 'undefined' || data._regions == null) {
            data._regions = [];
        }
        if (typeof(data._categories) == 'undefined' || data._categories == null) {
            data._categories = [];
        }
        if (typeof(data._seasons) == 'undefined' || data._seasons == null) {
            data._seasons = [];
        }
        for (var n=0,len=data._regions.length;n<len;n++) {
            if (typeof(data._regions[n]) == 'object') {
                data['regions[]'].push(data._regions[n].id);
            }
            else {
                data['regions[]'].push(data._regions[n]);
            }

        }
        for (var n=0,len=data._categories.length;n<len;n++) {
            if (typeof(data._categories[n]) == 'object') {
                data['categories[]'].push(data._categories[n].id);
            }
            else {
                data['categories[]'].push(data._categories[n]);
            }
        }

        for (var n=0,len=data._seasons.length;n<len;n++) {
            if (typeof(data._seasons[n]) == "object") {
                data[this.id + '_seasons'].push(data._seasons[n].id);
            }
            else {
                data[this.id + '_seasons'].push(data._seasons[n]);
            }

        }
        this.form.setValues(data);
    },

    /**
     * build
     * adds all the useful elements to the form
     */
    build : function() {
        var sdata = BQ.content.Util.getSeasons();

        var seasons = [];

        for (var n=0,len=sdata.length;n<len;n++) {
            seasons.push({xtype: 'checkbox', id: 'season_' + sdata[n].id, boxLabel: sdata[n].name, name: 'seasons[]', inputValue: sdata[n].id});
        }

        var items = [
            {xtype: 'hidden', name: 'id', anchor: '94%', allowBlank: false},
            {xtype: 'textfield', name: 'title', fieldLabel: App.t('title'), anchor: '94%', allowBlank: false, autoCreate: {tag: "input", type: "text", autocomplete: "off", maxlength: "30"}, emptyText: App.t('phrase_empty_text_title')},
            {xtype: 'textarea', name: 'description', fieldLabel: 'Description', anchor: '94%', maxLength: 2048, height:'160px'},
            {xtype: 'seasonsfield', id: this.id + '_seasons', name: this.id + '_seasons', fieldLabel: App.t('album_add_seasons'), anchor: '94%'},
            new BQ.content.RegionField({
                fieldLabel: App.t('album_add_regions')
            }),
            new BQ.content.CategoryField({
                fieldLabel: App.t('album_add_categories')
            }),
            {xtype: 'textfield', fieldLabel: App.t('album_add_tags'), name: 'tags', anchor: '94%', allowBlank: true, id:'tags_field'}
        ];

        return items;
    }


});


/**
 * @overview Map.js
 * A panel extension for displaying Korem google maps
 */
BQ.content.Map = Ext.extend(Ext.Panel, {
    /**
     * @cfg {HTMLElement} iframe
     * this panel iframes a google map from Korem, and the iframe attribute of the panel holds this DOM structure
     */
    iframe : null,
    
    /**
     * @cfg {String} url
     * iframe gets it's source data from this URL
     */
    url: null,
    
    /**
     * @cfg {String} contentId
     * keep track of the content ID 
     */
    contentId : null,

    initComponent : function() {

        /**
         * must be careful to ensure everything is rendered properly so we don't cause Browsers to error
         */
        this.on('show', function() {
            if (!this.rendered) {
                this.on('render', function(panel) { // <-- special case when not yet rendered
                    if (this.iframe == null) { this.buildMap(); this.init();}
                    
                },this);
            }
            else {
                this.init();
            }
        },this);

        /**
         * listen for a destroy event and make sure the iframe is truly removed from DOM
         */
        this.on('destroy', function() { // <-- remove iframe on destroy to prevent memory leak.
            if (this.iframe != null) {
                this.iframe.remove();
            }
        },this);

        
        // super
        BQ.content.Map.superclass.initComponent.call(this);
    },

    /**
     * buildMap
     * Create the iframe element based on what is known about Korem maps
     */
    buildMap : function() {
        // build iframe
        var iframe = document.createElement('iframe');
        iframe.scrolling = 'auto';
        iframe.width = '100%';
        iframe.name = 'korem';
        iframe.id = 'korem';
        iframe.frameBorder = 'no';

        this.iframe = iframe;

        this.body.dom.appendChild(this.iframe);
        this.iframe.height = this.body.getHeight() - 5;

        /***
         * @event resize
         * @param {Object} wnd
         * @param {Object} w
         * @param {Object} h
         */
        this.on('resize', function(wnd, w, h) {
            this.iframe.height = this.body.getHeight() - 5;
        },this);


    },

    /**
     * reset
     */
    reset : function() {
        this.contentId = null;
    },

    /**
     * setContentId
     * @param {Object} id
     */
    setContentId : function(id) {
        this.contentId = id;
    },

    /**
     * init
     * this is called when the component is instantiated. Could theoretically be enhanced to work as a plugin to some other container too...
     */
    init : function() {
        // si la map n'existe pas encore, on la crée
        if (this.iframe == null) { this.buildMap(); }
        // si la map qu'on souhaite afficher est différente de la map existante (cachée) (lien avec le 5930)
//        alert('this url = ' + this.url + ', et this.iframe.src= ' + this.iframe.src);
        if ((this.url+'&unitId='+this.contentId) != this.iframe.src) { this.update(); }
    },
    
    /**
    * Vincent Maillot vincent.maillot@cossette.com
    * update
    * permet de raffraichir la carte (lien avec le 5930)
    */
    update: function() {
        var url = this.url;
        if (this.contentId != null) { url += '&unitId=' + this.contentId; }  // <-- append unitId if contentId is set
        this.iframe.src = url;
        this.iframe.height = this.body.getHeight() -5;
    }
    
    
});
Ext.reg('contentmap', BQ.content.Map);

/**
 * BQ.content.Filter
 * @author Chris Scott
 * Reddit-style list filter
 *
 */
BQ.content.Filter = Ext.extend(Ext.Panel, {
    /**
     * @cfg {String} title
     * this panel has no useful title
     */
    title: '&nbsp;',
    
    /**
     * @cfg {String} id
     * the content filter has a pretty steady ID
     */
    id: 'content_filter',
    
    /**
     * @cfg {String} bodyStyle
     * add this css to the panel body css
     */
    bodyStyle: 'padding: 10px',
    
    /**
     * @cfg {Boolean} frame
     * do not dress this panel up with fancy form style
     */
    frame: false,
    
    /**
     * @cfg {Object} values 
     * data for the form
     */
    values : {},
    
    /**
     * @cfg {Boolean} buttonAlign
     * show the form buttons aligned to the middle of the container
     */
    buttonAlign: 'center',

    initComponent : function() {
        this.footer = true;
        this.addClass('bq-content-filter');
        this.items = this.build();
        this.tools = [{
            id: 'refresh',
            qtip: App.t('reset'),
            handler: this.reset,
            scope: this
        }];
        this.buttons = [
            {text: App.t('search'), handler: this.doFilter, scope: this}
        ];
        this.addEvents({
            /**
             * @event filter
             * fires the filter event with a value hash of selected regions, seasons, categories
             * @param {Object} value
             */
            filter: true
        });
        // super
        BQ.content.Filter.superclass.initComponent.call(this);
    },

    build : function() {
        var season = new BQ.content.SeasonsField({
            listeners : {
                scope: this
            }
        });
        var category = new BQ.content.CategoryField({
            listeners: {
                scope: this
            }
        });
        var region = new BQ.content.RegionField({
            listeners : {
                scope: this
            }
        });

        // set currently selected value
        if (typeof(this.values.categories) != 'undefined') {
            category.on('render', function() {
                category.setValue([this.values.categories]);
            },this);
        }
        else if (typeof(this.values.regions) != 'undefined') {
            region.on('render', function(){
                region.setValue([this.values.regions]);
            },this);
        }
        else if (typeof(this.values.saisons) != 'undefined') {
            season.on('render', function() {
                season.setValue([this.values.saisons]);
            },this);
        }

        return [{
                xtype: 'fieldset',
                title: App.t('categories'),
                autoHeight: true,
                autoWidth: true,
                hideLabels: true,
                items: category
            }, {
                xtype: 'fieldset',
                title: App.t('regions'),
                hideLabels: true,
                autoHeight: true,
                autoWidth: true,
                items: region
            }, {
                xtype: 'fieldset',
                title: App.t('seasons'),
                bodyStyle: 'padding: 0',
                hideLabels: true,
                autoHeight: true,
                autoWidth: true,
                items: season
            }
        ];
    },

  /*
  * Grabs the params chosen and does a new search. Pat was here.
  */
  doFilter : function() {


    this.fireEvent('filter', this.getValue());
  },

  getValue : function() {
      var fields = this.findBy(function(item) {
          return (item instanceof Ext.form.Field)
      });

      var filter = {};
      for (var n = 0, length = fields.length; n < length; n++) {
          filter[fields[n]['name']] = fields[n].getValue();
      }
      return filter;
  },

    reset : function(ev, el, panel) {
        var filter = {};
        this.suspendEvents();
        this.cascade(function(i) {
            if (i instanceof Ext.form.Field) {
                filter[i.name] = null
                i.reset();
            }
        });
        this.resumeEvents();

        // fire filter event to cause DataView to refresh itself with this emtpy filter
        this.fireEvent('filter', filter);
    }
})


/**
 * BQ.content.CdC
 * Coups de Couer
 * A Dataview with a store and a template, displaying content that qualifies for presentation in CdC
 * @author Chris Scott
 */
BQ.content.CdC = Ext.extend(RExt.DataView, {
    /**
     * @cfg {Boolean} frame
     * do not use fancy form style on this panel
     */
    frame: false,
    
    /**
     * @cfg {Boolean} autoHeight
     * let the render chain figure out the height of this panel
     */
    autoHeight: true,
    
    /**
     * @cfg {String} emptyText
     * if we have no records in the store, this is the text we want to display saying we're empty
     */
    emptyText: '<h1 class=\'no_results\'>' + App.t('no_results') + '</h1>',
    
    /**
     * @cfg {Object} recordType
     * the Ext.data.Record with all the goods for display 
     */
    recordType: BQ.data.Content,

    /**
     * @cfg {String} controller
     * the API script called by this component
     */
    controller: '/json/BQAContent',
    
    /**
     * @cfg {Object} baseParams
     * we ensure the calls to the API provide this needed information
     */
    baseParams : {
        types : 'photo,video',
        sort: 'rating DESC',
        heart : 1   // <-- tells server to provide Coups-de-coeur results only.
    },

    /**
     * @cfg {Boolean} mode [local]
     * whether to load static data or load from server
     */
    mode : 'local',

    /**
     * @cfg {Float, seconds} fadeDuration [1.5]
     * duration for fadeOut / fadeIn
     */
    fadeDuration : 1.5,

    /**
     * @cfg {Float, milliseconds} transitionDuration [5000]
     * duration between images.
     */
    transitionDuration : 5000,

    /**
     * @cfg {Number} viewSize [1]
     * the number of records to show simultaneously
     */
    viewSize: 1,

    initComponent : function() {
        this.addClass('cdc');
        BQ.content.CdC.superclass.initComponent.call(this);
    },

    /**
     * buildStore
     * generates a new store with supplied Store params.
     * @param {Object} config
     * @return {Ext.data.Store}
     */
    buildStore : function(config) {
        if (this.mode == 'local') {
            return new Ext.data.Store({
                autoLoad: true,
                reader: new Ext.data.ArrayReader({
                    id: 'id'
                }, BQ.data.Content),
                proxy: new Ext.data.MemoryProxy(BQ.data.CdC),
                listeners: {
                    load: this.run,
                    scope: this
                }
            });
        }
        else if (this.mode == 'remote') {
            // TODO: send-in supplied config param?
            return BQ.content.CdC.superclass.buildStore.call(this, {
                listeners: {
                    load: this.run,
                    scope: this
                },
                baseParams: this.baseParams
            });
        }
    },

    // disable toolbar by overriding build functions; return null
    buildTopToolbar : function() { return null; },
    buildBottomToolbar: function() { return null; },

    // private; start the transition
    run : function(store, rs, options) {
        // run away if resultset is empty
        if (rs.length == 0) {
            return false;
        }

        // set up the delayed-task environment.
        var total = rs.length;
        var index = -1;
        var view = this.getView();

        // for remote-mode only.  when view has had 60% of its contents displayed, it will buffer another
        // page of results.  this is similar behaviour to a "live-grid".
        var buffer  = null;
        var page    = 1;

        for (var n=0,len=this.viewSize;n<len;n++) {
            var el = Ext.get(view.getNode(n));
            if (el) { el.removeClass('x-hidden'); }

        }

        // Exec task!
        Ext.TaskMgr.start({
            run: function(){
                var prev = [];
                var next = [];

                // is this the very first run at the sequence?
                if (index < 0) {
                    index = 0;
                    return true;
                }

                // first get handles to *currently* selected node(s).  this will be prev
                for (var n=0;n<this.viewSize;n++) {
                    var el = view.getNode(index+n);

                    if (el) {
                        prev.push(Ext.get(el));
                    }
                }

                // increment the cursor
                index += this.viewSize;

                if (index >= total) {
                    index = 0;
                    if (buffer != null) {
                        // if there's a buffer, move it into store now.
                        store.removeAll();
                        store.add(buffer.records);
                        buffer = null;

                        for (var n=0;n<this.viewSize;n++) {
                            var el = view.getNode(index);
                            if (el) {
                                Ext.get(el).removeClass('x-hidden');
                            }
                        }
                    }
                }

                // if we're operating in remote mode, load buffer when view has iterated over >= 60%
                // so that a new set of records is waiting as soon as trasition has completed.

                if (this.mode == 'remote' && buffer == null && (index / this.pageSize >= 0.3) ) {
                    // load records using store's proxy and reader.

                    options.params.start = (store.getCount() == this.pageSize) ? page++ * this.pageSize : 0;

                    //console.log('page:', page);


                    // load
                    store.proxy.load(options.params, store.reader, function(res) {
                        buffer = res;

                       // console.log('buffer records:', buffer.records.length);
                        if (buffer.records.length < 3) { // if the buffer isn't full, revert to the old store, cause we're out of results
                              page = 0;
                              buffer = store.records;

                        }

                    });





                    // set loaded flag.  see IF conditions above.
                    loaded = true;
                }

                // finally, do the fade action.

                for (var n=0;n<this.viewSize;n++) {
                    var el = view.getNode(index+n);
                    if (el) { next.push(Ext.get(view.getNode(el))); }
                }
                //adding condition if gecko2, ie FF2 dont do fade cause it messes up the flash on homepage
                //
                if (!Ext.isGecko2) {
                view.el.fadeOut({
                    duration: this.fadeDuration,
                    callback : function() {
                        for (var n=0;n<this.viewSize;n++) {
                            if (prev[n]) { prev[n].addClass('x-hidden'); }
                            if (next[n]) { next[n].removeClass('x-hidden'); }
                        }
                        view.el.fadeIn({duration: this.fadeDuration});
                    },
                    scope: this
                });
                }
                else {
                        for (var n=0;n<this.viewSize;n++) {
                            if (prev[n]) { prev[n].addClass('x-hidden'); }
                            if (next[n]) { next[n].removeClass('x-hidden'); }
                        }
                }
            },
            scope : this,
            interval: this.transitionDuration //1 second
        });
    }
});
