odoo.define('web_unsplash.image_widgets', function (require) { 'use strict'; var core = require('web.core'); var UnsplashAPI = require('unsplash.api'); var widgetsMedia = require('wysiwyg.widgets.media'); var unsplashAPI = null; widgetsMedia.ImageWidget.include({ xmlDependencies: widgetsMedia.ImageWidget.prototype.xmlDependencies.concat( ['/web_unsplash/static/src/xml/unsplash_image_widget.xml'] ), events: _.extend({}, widgetsMedia.ImageWidget.prototype.events, { 'dblclick .unsplash_img_container [data-imgid]': '_onUnsplashImgDblClick', 'click .unsplash_img_container [data-imgid]': '_onUnsplashImgClick', 'click button.save_unsplash': '_onSaveUnsplash', 'click .o_search_an_image, .o_search_from_unsplash': '_onSourceSwitchClick', 'keyup .o_we_search, .o_search_an_image, .o_search_from_unsplash': '_onPressKeySearch', }), /** * @override */ init: function () { this._super.apply(this, arguments); this._unsplash = { isActive: false, selectedImages: {}, isMaxed: false, query: false, error: false, }; // TODO improve this // // This is a `hack` to prevent the UnsplashAPI to be destroyed every // time the media dialog is closed. Indeed, UnsplashAPI has a cache // system to recude unsplash call, it is then better to keep its state // to take advantage from it from one media dialog call to another. // // Unsplash API will either be (it's still being discussed): // * a service (ideally coming with an improvement to not auto load // the service) // * initialized in the website_root (trigger_up) if (unsplashAPI === null) { this.unsplashAPI = new UnsplashAPI(this); unsplashAPI = this.unsplashAPI; } else { this.unsplashAPI = unsplashAPI; this.unsplashAPI.setParent(this); } }, /** * @override */ start: function () { if (!this.options.document) { this.$('.o_we_search_icon').replaceWith(core.qweb.render('web_unsplash.dialog.media.search_icon')); } return this._super.apply(this, arguments); }, /** * @override */ destroy: function () { // TODO See `hack` explained in `init`. This prevent the media dialog destroy // to destroy unsplashAPI when destroying the children this.unsplashAPI.setParent(undefined); this._super.apply(this, arguments); }, // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- /** * @override */ save: function () { if (!this._unsplash.query) { return this._super.apply(this, arguments); } var self = this; var args = arguments; var _super = this._super; return this._rpc({ route: '/web_unsplash/attachment/add', params: { unsplashurls: self._unsplash.selectedImages, res_model: self.options.res_model, res_id: self.options.res_id, query: self._unsplash.query, } }).then(function (images) { _.each(images, function (image) { image.src = image.url; image.isDocument = !(/gif|jpe|jpg|png/.test(image.mimetype)); }); self.images = images; return _super.apply(self, args); }); }, /** * @override */ search: function (needle, noRender) { var self = this; if (!this._unsplash.isActive) { return this._super.apply(this, arguments); } this._unsplash.query = needle; var always = function () { if (!noRender) { self._renderImages(); self._adaptLoadMore(); } }; return this.unsplashAPI.getImages(needle, this.IMAGES_DISPLAYED_TOTAL).then(function (res) { self._unsplash.isMaxed = res.isMaxed; self._unsplash.records = res.images; self._unsplash.error = false; }, function (err) { self._unsplash.error = err; }).then(always).guardedCatch(always); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * @private */ _adaptLoadMore: function () { if (!this._unsplash.isActive) { return this._super.apply(this, arguments); } this.$('.o_load_more').toggleClass('d-none', !!this._unsplash.error || this._unsplash.isMaxed); this.$('.o_load_done_msg').toggleClass('d-none', !!this._unsplash.error || !this._unsplash.isMaxed); }, /** * @override */ _highlightSelectedImages: function () { var self = this; if (!this._unsplash.isActive) { return this._super.apply(this, arguments); } this.$('.o_unsplash_img_cell.o_selected').removeClass('o_selected'); var $select = this.$('.o_unsplash_img_cell [data-imgid]').filter(function () { return $(this).data('imgid') in self._unsplash.selectedImages; }); $select.closest('.o_unsplash_img_cell').addClass('o_selected'); return $select; }, /** * @private */ _loadMoreImages: function (forceSearch) { this._super(this._unsplash.isActive || forceSearch); }, /** * @override */ _renderImages: function () { var self = this; if (!this._unsplash.isActive) { return this._super.apply(this, arguments); } if (this._unsplash.error) { this.$('.unsplash_img_container').html( core.qweb.render('web_unsplash.dialog.error.content', { status: this._unsplash.error, }) ); return; } var rows = _(this._unsplash.records).chain() .groupBy(function (a, index) { return Math.floor(index / self.IMAGES_PER_ROW); }) .values() .value(); this.$('.unsplash_img_container').html(core.qweb.render('web_unsplash.dialog.image.content', {rows: rows})); this._highlightSelectedImages(); }, /** * @private * @param {boolean} show */ _toggleUnsplashContainer: function (show) { this._unsplash.isActive = show; this.$('.existing-attachments').toggleClass('d-none', show); this.$('.unsplash_img_container').toggleClass('d-none', !show); this.$('.o_we_search_icon > span').text(show ? "Unsplash" : ""); }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * @private */ _onSaveUnsplash: function () { var self = this; var key = this.$('#accessKeyInput').val().trim(); var appId = this.$('#appIdInput').val().trim(); this.$('#accessKeyInput').toggleClass('is-invalid', !key); this.$('#appIdInput').toggleClass('is-invalid', !appId); if (key && appId) { var params = { 'key': key, 'appId': appId, }; if (!this.$el.find('.is-invalid').length) { this._rpc({ route: '/web_unsplash/save_unsplash', params: params, }).then(function () { self.unsplashAPI.clientId = key; self._unsplash.error = false; self.search(self._unsplash.query); }); } } }, /** * @private */ _onUnsplashImgClick: function (ev) { var imgid = $(ev.currentTarget).data('imgid'); var url = $(ev.currentTarget).data('url'); var downloadURL = $(ev.currentTarget).data('download-url'); if (!this.multiImages) { this._unsplash.selectedImages = {}; } if (imgid in this._unsplash.selectedImages) { delete this._unsplash.selectedImages[imgid]; } else { this._unsplash.selectedImages[imgid] = {url: url, download_url: downloadURL}; } this._highlightSelectedImages(); }, /** * @private */ _onUnsplashImgDblClick: function (ev) { this.trigger_up('save_request'); }, /** * @private * @param {Event} ev */ _onSourceSwitchClick: function (ev) { this._toggleUnsplashContainer(!$(ev.currentTarget).is('.o_search_an_image')); this.search(this.$('.o_we_search').val() || ''); }, /** * @private */ _onPressKeySearch: function (ev) { switch (ev.which) { case $.ui.keyCode.UP: this.$('.o_search_an_image').focus(); break; case $.ui.keyCode.DOWN: this.$('.o_search_from_unsplash').focus(); break; default: return; } ev.preventDefault(); }, /** * @override */ _onSearchInput: function (ev) { if (!this.options.document) { var $input = $(ev.currentTarget); var inputValue = $input.val(); this.$('.o_search_value_input').text(inputValue); var $icon = this.$('.o_we_search_icon'); if ($icon.parent().is('.show')) { $icon.dropdown('update'); } else if (!this.hasSearched) { $icon.dropdown('toggle'); $input.focus(); } } this._super.apply(this, arguments); }, }); });