diff --git a/music_player/__manifest__.py b/music_player/__manifest__.py index c004329..2e49dbf 100644 --- a/music_player/__manifest__.py +++ b/music_player/__manifest__.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- { - 'name': "musicPlayer", + 'name': "MusicPlayer", - 'summary': """ - Short (1 phrase/line) summary of the module's purpose, used as - subtitle on modules listing or apps.openerp.com""", + 'summary': " ", + 'License':'LGPL-3', 'description': """ Long description of module's purpose @@ -28,10 +27,15 @@ 'views/views.xml', 'views/templates.xml', ], + 'assets':{ + 'web.assets_backend':[ + 'music_player/static/app.js' + ], + }, # only loaded in demonstration mode 'demo': [ 'demo/demo.xml', ], - 'installable': True, - 'application': True, + 'installable':True, + 'application':True, } diff --git a/music_player/__pycache__/__init__.cpython-310.pyc b/music_player/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 17b583d..0000000 Binary files a/music_player/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/music_player/__pycache__/__init__.cpython-38.pyc b/music_player/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..28f110a Binary files /dev/null and b/music_player/__pycache__/__init__.cpython-38.pyc differ diff --git a/music_player/controllers/__pycache__/__init__.cpython-310.pyc b/music_player/controllers/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 28642f0..0000000 Binary files a/music_player/controllers/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/music_player/controllers/__pycache__/__init__.cpython-38.pyc b/music_player/controllers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..19e0da0 Binary files /dev/null and b/music_player/controllers/__pycache__/__init__.cpython-38.pyc differ diff --git a/music_player/controllers/__pycache__/controllers.cpython-310.pyc b/music_player/controllers/__pycache__/controllers.cpython-310.pyc deleted file mode 100644 index 8adc616..0000000 Binary files a/music_player/controllers/__pycache__/controllers.cpython-310.pyc and /dev/null differ diff --git a/music_player/controllers/__pycache__/controllers.cpython-38.pyc b/music_player/controllers/__pycache__/controllers.cpython-38.pyc new file mode 100644 index 0000000..82a00b6 Binary files /dev/null and b/music_player/controllers/__pycache__/controllers.cpython-38.pyc differ diff --git a/music_player/controllers/controllers.py b/music_player/controllers/controllers.py index 9f470b2..224b5d5 100644 --- a/music_player/controllers/controllers.py +++ b/music_player/controllers/controllers.py @@ -1,29 +1,23 @@ -# -*- coding: utf-8 -*- -import json +#-- coding: utf-8 -- from odoo import http -from odoo.http import Response from odoo.modules.module import get_module_resource +import json class MusicPlayer(http.Controller): - @http.route('/music', auth='public') - def index(self, **kw): - return http.request.render('music_player.music_template') - - @http.route('/music/search', auth='public', type="http", methods=["GET"]) - def search(self, **kw): - # Retrieve the song name from the search query - song_name = kw.get('song_name') - # you will be facing you are not allowed to acces this model ---> add to manifest the csv file. remove group for now - musics = http.request.env['music_player.music_player'].search_read([('name', 'ilike', song_name)],fields={"name", "url"}) - if not musics: - musics = "Song not Found" - - return Response(json.dumps({'result': musics}), content_type='application/json') + @http.route('/music', auth='public') + def index(self, **kw): + return http.request.render('music_player.music_template') - # A controller to play song from the audio + @http.route('/music/search', auth='public',type="http",methods=['GET']) + def search(self, **kw): + song_name = kw.get('song_name') + songs = http.request.env['music_player.music_player'].search_read([('name','ilike',song_name)],fields=["name","url"]) + if not songs: + songs = "Song not found." + return http.Response(json.dumps({'result':songs}),content_type='application/json') - @http.route('/music/', type='http', auth="public", methods=["GET"]) - def load(self, music, **kw): - music_file_path = get_module_resource('music_player', 'static/songs', music.filename) - file = open(music_file_path, 'rb').read() - return file + @http.route('/music/',type="http",auth="public",methods=['GET']) + def load(self,music,**kw): + music_file_path = get_module_resource('music_player','static/songs',music.filename) + file = open(music_file_path,'rb').read() + return file \ No newline at end of file diff --git a/music_player/demo/demo.xml b/music_player/demo/demo.xml index f13bd4c..4c9a3c0 100644 --- a/music_player/demo/demo.xml +++ b/music_player/demo/demo.xml @@ -33,4 +33,4 @@ Temp-song-3 Temp-song-3.mp3 - \ No newline at end of file + diff --git a/music_player/models/__pycache__/__init__.cpython-310.pyc b/music_player/models/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 480c7d7..0000000 Binary files a/music_player/models/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/music_player/models/__pycache__/__init__.cpython-38.pyc b/music_player/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..1e56d27 Binary files /dev/null and b/music_player/models/__pycache__/__init__.cpython-38.pyc differ diff --git a/music_player/models/__pycache__/models.cpython-310.pyc b/music_player/models/__pycache__/models.cpython-310.pyc deleted file mode 100644 index 01fe441..0000000 Binary files a/music_player/models/__pycache__/models.cpython-310.pyc and /dev/null differ diff --git a/music_player/models/__pycache__/models.cpython-38.pyc b/music_player/models/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000..2b68e27 Binary files /dev/null and b/music_player/models/__pycache__/models.cpython-38.pyc differ diff --git a/music_player/models/__pycache__/player.cpython-310.pyc b/music_player/models/__pycache__/player.cpython-310.pyc deleted file mode 100644 index 34917a7..0000000 Binary files a/music_player/models/__pycache__/player.cpython-310.pyc and /dev/null differ diff --git a/music_player/models/__pycache__/player.cpython-38.pyc b/music_player/models/__pycache__/player.cpython-38.pyc new file mode 100644 index 0000000..9fdf75a Binary files /dev/null and b/music_player/models/__pycache__/player.cpython-38.pyc differ diff --git a/music_player/models/player.py b/music_player/models/player.py index a7e7df7..9b0c0c9 100644 --- a/music_player/models/player.py +++ b/music_player/models/player.py @@ -8,9 +8,9 @@ class music_player(models.Model): _description = 'music_player.music_player' name = fields.Char('Song Name') - filename = fields.Char("File name") - url = fields.Char(compute="_compute_url") # for a computed url - + filename = fields.Char('File Name') + url = fields.Char( compute ="_compute_url") + def _compute_url(self): for record in self: - record.url = record.get_base_url() + '/music/' + str(record.id) + record.url = record.get_base_url() + '/music/' + str(record.id) \ No newline at end of file diff --git a/music_player/static/app.js b/music_player/static/app.js index 103f245..cd5d4e9 100644 --- a/music_player/static/app.js +++ b/music_player/static/app.js @@ -1,129 +1,150 @@ /** @odoo-module**/ -const { Component, xml, mount, setup, useState } = owl; +const { Component,xml,mount,useState,onWillStart } = owl; -let audio = ''; +let audio=''; class Player extends Component { static template = xml`

Song Title

- +
-
`; - - playThisSong() { - if (!audio) { - return; - } - audio.play(); - } - pauseThisSong() { - if (!audio) { - return; - } - audio.pause(); - } - stopThisSong() { - if (!audio) { - return; - } - audio.pause(); - audio.currentTime = 0; + + `; + playThisSong(){ + if(!audio){ + return; + } + audio.play(); + } + pauseThisSong(){ + if(!audio){ + return; + } + audio.pause(); + } + stopThisSong(){ + if(!audio){ + return; } + audio.pause(); + audio.currentTime=0; + } } -// class PlayList extends Component { -// static template = xml` -//
+class PlayList extends Component { + static template = xml` +
+

Playlist

+ + +

+ + +
+
+
+ `; -//
-// `; + removeSongFromPlayList(ev) { + const selectedSongUrl = ev.target.getAttribute('value'); + const selectedSong = this.props.playData.findIndex(song => song.url===selectedSongUrl); + this.props.playData.splice(selectedSong,1); + } -// } + playSong(ev) { + if (audio) { + audio.pause(); + audio.currentTime=0; + } + const selectedSongUrl = ev.target.getAttribute('value'); + const selectedSong = this.props.playData.find(song => song.url===selectedSongUrl); + document.getElementById("song-title").textContent = selectedSong.name; + audio = new Audio(selectedSongUrl); + audio.play(); + } +} -class MusicList extends Component { +class List extends Component { static template = xml`
- -

List of Songs

- -

- - -
-
- + +

List of Songs

+ +

+ + +
+
+
- `; + `; - addSongToPlaylist () { - //TAsk: - // add Playlist component as the child of root component and when addSongToPlaylist method is called update the - //PlayList component template with the song thats added. - // hint use callback method as which update the props u are passing to PlayList component. + playSong(ev) { + if (audio) { + audio.pause(); + audio.currentTime=0; } + const selectedSongUrl = ev.target.getAttribute('value'); + const selectedSong = this.props.searchData[0].find(song => song.url===selectedSongUrl); + document.getElementById("song-title").textContent = selectedSong.name; + audio = new Audio(selectedSongUrl); + audio.play(); + } + + addSongToPlaylist(ev) { - playSong(ev) { - // in case a audio is already playing stop it to play another. - if (audio) { - audio.pause(); - audio.currentTime = 0; - } - const selectedSongUrl = ev.target.getAttribute('value'); - const selectedSong = this.props.searchData[0].find(song => song.url === selectedSongUrl); - document.getElementById('song-title').textContent = selectedSong.name; - audio = new Audio(selectedSongUrl); - audio.play(); - } - static props = ['searchData']; - - static components = { Player }; + const selectedSongUrl = ev.target.getAttribute('value'); + const selectedSong = this.props.searchData[0].find(song => song.url===selectedSongUrl); + this.props.addToPlayList(selectedSong); + } + + static components = {Player}; } class Search extends Component { - static template = xml ` -
- + static template = xml` +
+ - -
+ +
`; + setup() { this.searchData = useState([]); } async getMusic() { - const findSong = document.getElementById('searchSong').value; + const findSong = document.getElementById("searchSong").value; const response = await fetch(`/music/search?song_name=${findSong}`); - const {result : newData}= await response.json(); - this.searchData.pop(); // add pop to remove previously searched data. + const {result: newData} = await response.json(); this.searchData.push(newData); } - - static components = { MusicList } + static components = {List}; } -class Root extends Component { // import from owl - // import from owl - static template = xml ` - -
- +class Root extends Component { + static template = xml` +
+ +
`; - static components = { Search }; + setup() { + this.playData = useState([]); + } + + addToPlayList(song) { + this.playData.push(song); + } + static components = {Search,PlayList}; } window.onload = function() { - mount(Root, document.body); + mount(Root,document.body); }; \ No newline at end of file diff --git a/music_player/static/owl.js b/music_player/static/owl.js index 7e024b6..b07d8c8 100644 --- a/music_player/static/owl.js +++ b/music_player/static/owl.js @@ -1,6 +1,6 @@ -(function (exports) { - 'use strict'; - +(function (exports) { + 'use strict'; + function filterOutModifiersFromData(dataList) { dataList = dataList.slice(); const modifiers = []; @@ -27,8 +27,8 @@ } return false; }, - }; - + }; + // ----------------------------------------------------------------------------- // Toggler node // ----------------------------------------------------------------------------- @@ -81,8 +81,8 @@ } function toggler(key, child) { return new VToggler(key, child); - } - + } + // Custom error class that wraps error that happen in the owl lifecycle class OwlError extends Error { } @@ -144,8 +144,8 @@ } throw error; } - } - + } + const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype; const tokenList = DOMTokenList.prototype; const tokenListAdd = tokenList.add; @@ -279,34 +279,8 @@ tokenListAdd.call(cl, c); } } - } - function makePropSetter(name) { - return function setProp(value) { - // support 0, fallback to empty string for other falsy values - this[name] = value === 0 ? 0 : value ? value.valueOf() : ""; - }; - } - function isProp(tag, key) { - switch (tag) { - case "input": - return (key === "checked" || - key === "indeterminate" || - key === "value" || - key === "readonly" || - key === "disabled"); - case "option": - return key === "selected" || key === "disabled"; - case "textarea": - return key === "value" || key === "readonly" || key === "disabled"; - case "select": - return key === "value" || key === "disabled"; - case "button": - case "optgroup": - return key === "disabled"; - } - return false; - } - + } + function createEventHandler(rawEvent) { const eventName = rawEvent.split(".")[0]; const capture = rawEvent.includes(".capture"); @@ -389,8 +363,8 @@ capture, }); CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true; - } - + } + const getDescriptor$3 = (o, p) => Object.getOwnPropertyDescriptor(o, p); const nodeProto$4 = Node.prototype; const nodeInsertBefore$3 = nodeProto$4.insertBefore; @@ -528,8 +502,8 @@ } function multi(children) { return new VMulti(children); - } - + } + const getDescriptor$2 = (o, p) => Object.getOwnPropertyDescriptor(o, p); const nodeProto$3 = Node.prototype; const characterDataProto$1 = CharacterData.prototype; @@ -598,8 +572,8 @@ default: return value || ""; } - } - + } + const getDescriptor$1 = (o, p) => Object.getOwnPropertyDescriptor(o, p); const nodeProto$2 = Node.prototype; const elementProto = Element.prototype; @@ -608,6 +582,12 @@ const nodeGetFirstChild = getDescriptor$1(nodeProto$2, "firstChild").get; const nodeGetNextSibling = getDescriptor$1(nodeProto$2, "nextSibling").get; const NO_OP = () => { }; + function makePropSetter(name) { + return function setProp(value) { + // support 0, fallback to empty string for other falsy values + this[name] = value === 0 ? 0 : value ? value.valueOf() : ""; + }; + } const cache$1 = {}; /** * Compiling blocks is a multi-step process: @@ -725,6 +705,15 @@ tag: tagName, }); } + else if (attrName.startsWith("block-property-")) { + const idx = parseInt(attrName.slice(15), 10); + info.push({ + type: "property", + idx, + name: attrValue, + tag: tagName, + }); + } else if (attrName === "block-attributes") { info.push({ type: "attributes", @@ -871,16 +860,22 @@ }; } break; + case "property": { + const refIdx = info.refIdx; + const setProp = makePropSetter(info.name); + ctx.locations.push({ + idx: info.idx, + refIdx, + setData: setProp, + updateData: setProp, + }); + break; + } case "attribute": { const refIdx = info.refIdx; let updater; let setter; - if (isProp(info.tag, info.name)) { - const setProp = makePropSetter(info.name); - setter = setProp; - updater = setProp; - } - else if (info.name === "class") { + if (info.name === "class") { setter = setClass; updater = updateClass; } @@ -1105,8 +1100,8 @@ return function setRef(fn) { refs[refs.length - 1][index] = () => fn(this); }; - } - + } + const getDescriptor = (o, p) => Object.getOwnPropertyDescriptor(o, p); const nodeProto$1 = Node.prototype; const nodeInsertBefore$1 = nodeProto$1.insertBefore; @@ -1322,8 +1317,8 @@ mapping[ch1[i].key] = i; } return mapping; - } - + } + const nodeProto = Node.prototype; const nodeInsertBefore = nodeProto.insertBefore; const nodeRemoveChild = nodeProto.removeChild; @@ -1398,8 +1393,8 @@ } function html(str) { return new VHtml(str); - } - + } + function createCatcher(eventsSpec) { const n = Object.keys(eventsSpec).length; class VCatcher { @@ -1486,8 +1481,8 @@ return function (child, handlers) { return new VCatcher(child, handlers); }; - } - + } + function mount$1(vnode, fixture, afterNode = null) { vnode.mount(fixture, afterNode); } @@ -1499,8 +1494,8 @@ vnode.beforeRemove(); } vnode.remove(); - } - + } + function makeChildFiber(node, parent) { let current = node.fiber; if (current) { @@ -1741,8 +1736,8 @@ this.node.app.handleError({ fiber: current, error: e }); } } - } - + } + // Special key to subscribe to, to be notified of key creation/deletion const KEYCHANGES = Symbol("Key changes"); // Used to specify the absence of a callback, can be used as WeakMap key but @@ -1888,10 +1883,15 @@ const targets = callbacksToTargets.get(callback) || []; return [...targets].map((target) => { const keysToCallbacks = targetToKeysToCallbacks.get(target); - return { - target, - keys: keysToCallbacks ? [...keysToCallbacks.keys()] : [], - }; + let keys = []; + if (keysToCallbacks) { + for (const [key, cbs] of keysToCallbacks) { + if (cbs.has(callback)) { + keys.push(key); + } + } + } + return { target, keys }; }); } // Maps reactive objects to the underlying target @@ -2163,8 +2163,8 @@ return possiblyReactive(target[key], callback); }, }); - } - + } + /** * Creates a batched version of a callback so that all calls to it in the same * microtick will only call the original callback once. @@ -2239,8 +2239,8 @@ */ function markup(value) { return new Markup(value); - } - + } + let currentNode = null; function getCurrent() { if (!currentNode) { @@ -2480,6 +2480,18 @@ this.fiber = null; } } + /** + * Sets a ref to a given HTMLElement. + * + * @param name the name of the ref to set + * @param el the HTMLElement to set the ref to. The ref is not set if the el + * is null, but useRef will not return elements that are not in the DOM + */ + setRef(name, el) { + if (el) { + this.refs[name] = el; + } + } // --------------------------------------------------------------------------- // Block DOM methods // --------------------------------------------------------------------------- @@ -2539,8 +2551,8 @@ const render = batchedRenderFunctions.get(this); return render ? getSubscriptions(render) : []; } - } - + } + const TIMEOUT = Symbol("timeout"); function wrapError(fn, hookName) { const error = new OwlError(`The following error occurred in ${hookName}: `); @@ -2647,8 +2659,8 @@ nodeErrorHandlers.set(node, handlers); } handlers.push(callback.bind(node.component)); - } - + } + class Component { constructor(props, env, node) { this.props = props; @@ -2660,8 +2672,8 @@ this.__owl__.render(deep === true); } } - Component.template = ""; - + Component.template = ""; + const VText = text("").constructor; class VPortal extends VText { constructor(selector, content) { @@ -2737,8 +2749,8 @@ type: String, }, slots: true, - }; - + }; + // ----------------------------------------------------------------------------- // helpers // ----------------------------------------------------------------------------- @@ -2789,6 +2801,7 @@ if (Array.isArray(schema)) { schema = toSchema(schema); } + obj = toRaw(obj); let errors = []; // check if each value in obj has correct shape for (let key in obj) { @@ -2875,8 +2888,8 @@ result = !descr.validate(value) ? `'${key}' is not valid` : null; } return result; - } - + } + const ObjectCreate = Object.create; /** * This file contains utility functions that will be injected in each template, @@ -3014,36 +3027,6 @@ } return toggler(safeKey, block); } - let boundFunctions = new WeakMap(); - const WeakMapGet = WeakMap.prototype.get; - const WeakMapSet = WeakMap.prototype.set; - function bind(component, fn) { - let boundFnMap = WeakMapGet.call(boundFunctions, component); - if (!boundFnMap) { - boundFnMap = new WeakMap(); - WeakMapSet.call(boundFunctions, component, boundFnMap); - } - let boundFn = WeakMapGet.call(boundFnMap, fn); - if (!boundFn) { - boundFn = fn.bind(component); - WeakMapSet.call(boundFnMap, fn, boundFn); - } - return boundFn; - } - function multiRefSetter(refs, name) { - let count = 0; - return (el) => { - if (el) { - count++; - if (count > 1) { - throw new OwlError("Cannot have 2 elements with same ref name at the same time"); - } - } - if (count === 0 || el) { - refs[name] = el; - } - }; - } /** * Validate the component props (or next props) against the (static) props * description. This is potentially an expensive operation: it may needs to @@ -3082,6 +3065,16 @@ throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", ")); } } + function makeRefWrapper(node) { + let refNames = new Set(); + return (name, fn) => { + if (refNames.has(name)) { + throw new OwlError(`Cannot set the same ref more than once in the same component, ref "${name}" was set multiple times in ${node.name}`); + } + refNames.add(name); + return fn; + }; + } const helpers = { withDefault, zero: Symbol("zero"), @@ -3091,18 +3084,17 @@ withKey, prepareList, setContextValue, - multiRefSetter, shallowEqual, toNumber, validateProps, LazyValue, safeOutput, - bind, createCatcher, markRaw, OwlError, - }; - + makeRefWrapper, + }; + const bdom = { text, createBlock, list, multi, html, toggler, comment }; function parseXML$1(xml) { const parser = new DOMParser(); @@ -3218,8 +3210,8 @@ return name; } xml.nextId = 1; - TemplateSet.registerTemplate("__portal__", portalTemplate); - + TemplateSet.registerTemplate("__portal__", portalTemplate); + /** * Owl QWeb Expression Parser * @@ -3525,8 +3517,9 @@ } function interpolate(s) { return replaceDynamicParts(s, compileExpr); - } - + } + + const whitespaceRE = /\s+/g; // using a non-html document so that HTML serializes as XML instead // of HTML (as we will parse it as xml later) const xmlDoc = document.implementation.createDocument(null, null, null); @@ -3536,6 +3529,27 @@ nextDataIds[prefix] = (nextDataIds[prefix] || 0) + 1; return prefix + nextDataIds[prefix]; } + function isProp(tag, key) { + switch (tag) { + case "input": + return (key === "checked" || + key === "indeterminate" || + key === "value" || + key === "readonly" || + key === "readOnly" || + key === "disabled"); + case "option": + return key === "selected" || key === "disabled"; + case "textarea": + return key === "value" || key === "readonly" || key === "readOnly" || key === "disabled"; + case "select": + return key === "value" || key === "disabled"; + case "button": + case "optgroup": + return key === "disabled"; + } + return false; + } // ----------------------------------------------------------------------------- // BlockDescription // ----------------------------------------------------------------------------- @@ -3611,10 +3625,8 @@ this.code = []; this.hasRoot = false; this.hasCache = false; - this.hasRef = false; - // maps ref name to [id, expr] - this.refInfo = {}; this.shouldProtectScope = false; + this.hasRefWrapper = false; this.name = name; this.on = on || null; } @@ -3630,17 +3642,13 @@ generateCode() { let result = []; result.push(`function ${this.name}(ctx, node, key = "") {`); - if (this.hasRef) { - result.push(` const refs = this.__owl__.refs;`); - for (let name in this.refInfo) { - const [id, expr] = this.refInfo[name]; - result.push(` const ${id} = ${expr};`); - } - } if (this.shouldProtectScope) { result.push(` ctx = Object.create(ctx);`); result.push(` ctx[isBoundary] = 1`); } + if (this.hasRefWrapper) { + result.push(` let refWrapper = makeRefWrapper(this.__owl__);`); + } if (this.hasCache) { result.push(` let cache = ctx.cache || {};`); result.push(` let nextCache = ctx.cache = {};`); @@ -3922,6 +3930,9 @@ const match = translationRE.exec(value); value = match[1] + this.translateFn(match[2]) + match[3]; } + if (!ctx.inPreTag) { + value = value.replace(whitespaceRE, " "); + } if (!block || forceNewBlock) { block = this.createBlock(block, "text", ctx); this.insertBlock(`text(\`${value}\`)`, block, { @@ -3986,21 +3997,29 @@ attrName = key === "t-att" ? null : key.slice(6); expr = compileExpr(ast.attrs[key]); if (attrName && isProp(ast.tag, attrName)) { + if (attrName === "readonly") { + // the property has a different name than the attribute + attrName = "readOnly"; + } // we force a new string or new boolean to bypass the equality check in blockdom when patching same value if (attrName === "value") { - // When the expression is falsy, fall back to an empty string - expr = `new String((${expr}) || "")`; + // When the expression is falsy (except 0), fall back to an empty string + expr = `new String((${expr}) === 0 ? 0 : ((${expr}) || ""))`; } else { expr = `new Boolean(${expr})`; } - } - const idx = block.insertData(expr, "attr"); - if (key === "t-att") { - attrs[`block-attributes`] = String(idx); + const idx = block.insertData(expr, "prop"); + attrs[`block-property-${idx}`] = attrName; } else { - attrs[`block-attribute-${idx}`] = attrName; + const idx = block.insertData(expr, "attr"); + if (key === "t-att") { + attrs[`block-attributes`] = String(idx); + } + else { + attrs[`block-attribute-${idx}`] = attrName; + } } } else if (this.translatableAttributes.includes(key)) { @@ -4037,8 +4056,8 @@ targetExpr = compileExpr(dynamicTgExpr); } } - idx = block.insertData(`${fullExpression} === ${targetExpr}`, "attr"); - attrs[`block-attribute-${idx}`] = specialInitTargetAttr; + idx = block.insertData(`${fullExpression} === ${targetExpr}`, "prop"); + attrs[`block-property-${idx}`] = specialInitTargetAttr; } else if (hasDynamicChildren) { const bValueId = generateId("bValue"); @@ -4046,8 +4065,8 @@ this.define(tModelSelectedExpr, fullExpression); } else { - idx = block.insertData(`${fullExpression}`, "attr"); - attrs[`block-attribute-${idx}`] = targetAttr; + idx = block.insertData(`${fullExpression}`, "prop"); + attrs[`block-property-${idx}`] = targetAttr; } this.helpers.add("toNumber"); let valueCode = `ev.target.${targetAttr}`; @@ -4065,30 +4084,21 @@ } // t-ref if (ast.ref) { - this.target.hasRef = true; + if (this.dev) { + this.helpers.add("makeRefWrapper"); + this.target.hasRefWrapper = true; + } const isDynamic = INTERP_REGEXP.test(ast.ref); + let name = `\`${ast.ref}\``; if (isDynamic) { - const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true)); - const idx = block.insertData(`(el) => refs[${str}] = el`, "ref"); - attrs["block-ref"] = String(idx); + name = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true)); } - else { - let name = ast.ref; - if (name in this.target.refInfo) { - // ref has already been defined - this.helpers.add("multiRefSetter"); - const info = this.target.refInfo[name]; - const index = block.data.push(info[0]) - 1; - attrs["block-ref"] = String(index); - info[1] = `multiRefSetter(refs, \`${name}\`)`; - } - else { - let id = generateId("ref"); - this.target.refInfo[name] = [id, `(el) => refs[\`${name}\`] = el`]; - const index = block.data.push(id) - 1; - attrs["block-ref"] = String(index); - } + let setRefStr = `(el) => this.__owl__.setRef((${name}), el)`; + if (this.dev) { + setRefStr = `refWrapper(${name}, ${setRefStr})`; } + const idx = block.insertData(setRefStr, "ref"); + attrs["block-ref"] = String(idx); } const dom = xmlDoc.createElement(ast.tag); for (const [attr, val] of Object.entries(attrs)) { @@ -4111,6 +4121,7 @@ tKeyExpr: ctx.tKeyExpr, nameSpace, tModelSelectedExpr, + inPreTag: ctx.inPreTag || ast.tag === "pre", }); this.compileAST(child, subCtx); } @@ -4495,13 +4506,15 @@ value = this.captureExpression(value); if (name.includes(".")) { let [_name, suffix] = name.split("."); - if (suffix === "bind") { - this.helpers.add("bind"); - name = _name; - value = `bind(this, ${value || undefined})`; - } - else { - throw new OwlError("Invalid prop suffix"); + name = _name; + switch (suffix) { + case "bind": + value = `(${value}).bind(this)`; + break; + case "alike": + break; + default: + throw new OwlError("Invalid prop suffix"); } } name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`; @@ -4588,10 +4601,24 @@ keyArg = `${ctx.tKeyExpr} + ${keyArg}`; } let id = generateId("comp"); + const propList = []; + for (let p in ast.props || {}) { + let [name, suffix] = p.split("."); + if (!suffix) { + propList.push(`"${name}"`); + } + } this.staticDefs.push({ id, - expr: `app.createComponent(${ast.isDynamic ? null : expr}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, ${!ast.props && !ast.dynamicProps})`, + expr: `app.createComponent(${ast.isDynamic ? null : expr}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, [${propList}])`, }); + if (ast.isDynamic) { + // If the component class changes, this can cause delayed renders to go + // through if the key doesn't change. Use the component name for now. + // This means that two component classes with the same name isn't supported + // in t-component. We can generate a unique id per class later if needed. + keyArg = `(${expr}).name + ${keyArg}`; + } let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`; if (ast.isDynamic) { blockExpr = `toggler(${expr}, ${blockExpr})`; @@ -4704,8 +4731,8 @@ this.insertBlock(blockString, block, { ...ctx, forceNewBlock: false }); return block.varName; } - } - + } + // ----------------------------------------------------------------------------- // Parser // ----------------------------------------------------------------------------- @@ -4761,15 +4788,11 @@ // Text and Comment Nodes // ----------------------------------------------------------------------------- const lineBreakRE = /[\r\n]/; - const whitespaceRE = /\s+/g; function parseTextCommentNode(node, ctx) { if (node.nodeType === Node.TEXT_NODE) { let value = node.textContent || ""; - if (!ctx.inPreTag) { - if (lineBreakRE.test(value) && !value.trim()) { - return null; - } - value = value.replace(whitespaceRE, " "); + if (!ctx.inPreTag && lineBreakRE.test(value) && !value.trim()) { + return null; } return { type: 0 /* Text */, value }; } @@ -5441,8 +5464,8 @@ throw new OwlError(msg); } return doc; - } - + } + function compile(template, options = {}) { // parsing const ast = parse(template); @@ -5455,53 +5478,11 @@ const code = codeGenerator.generateCode(); // template function return new Function("app, bdom, helpers", code); - } - - const mainEventHandler = (data, ev, currentTarget) => { - const { data: _data, modifiers } = filterOutModifiersFromData(data); - data = _data; - let stopped = false; - if (modifiers.length) { - let selfMode = false; - const isSelf = ev.target === currentTarget; - for (const mod of modifiers) { - switch (mod) { - case "self": - selfMode = true; - if (isSelf) { - continue; - } - else { - return stopped; - } - case "prevent": - if ((selfMode && isSelf) || !selfMode) - ev.preventDefault(); - continue; - case "stop": - if ((selfMode && isSelf) || !selfMode) - ev.stopPropagation(); - stopped = true; - continue; - } - } - } - // If handler is empty, the array slot 0 will also be empty, and data will not have the property 0 - // We check this rather than data[0] being truthy (or typeof function) so that it crashes - // as expected when there is a handler expression that evaluates to a falsy value - if (Object.hasOwnProperty.call(data, 0)) { - const handler = data[0]; - if (typeof handler !== "function") { - throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`); - } - let node = data[1] ? data[1].__owl__ : null; - if (node ? node.status === 1 /* MOUNTED */ : true) { - handler.call(node ? node.component : null, ev); - } - } - return stopped; - }; - + } + + // do not modify manually. This value is updated by the release script. + const version = "2.0.9"; + // ----------------------------------------------------------------------------- // Scheduler // ----------------------------------------------------------------------------- @@ -5565,24 +5546,27 @@ } // capture the value of requestAnimationFrame as soon as possible, to avoid // interactions with other code, such as test frameworks that override them - Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window); - + Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window); + let hasBeenLogged = false; const DEV_MSG = () => { const hash = window.owl ? window.owl.__info__.hash : "master"; - return `Owl is running in 'dev' mode. - -This is not suitable for production use. + return `Owl is running in 'dev' mode. + +This is not suitable for production use. See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`; }; window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps: new Set(), + Fiber: Fiber, + RootFiber: RootFiber, }); class App extends TemplateSet { constructor(Root, config = {}) { super(config); this.scheduler = new Scheduler(); this.root = null; + this.name = config.name || ""; this.Root = Root; window.__OWL_DEVTOOLS__.apps.add(this); if (config.test) { @@ -5643,21 +5627,36 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration } window.__OWL_DEVTOOLS__.apps.delete(this); } - createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, hasNoProp) { + createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, propList) { const isDynamic = !isStatic; - function _arePropsDifferent(props1, props2) { - for (let k in props1) { - if (props1[k] !== props2[k]) { - return true; + let arePropsDifferent; + const hasNoProp = propList.length === 0; + if (hasSlotsProp) { + arePropsDifferent = (_1, _2) => true; + } + else if (hasDynamicPropList) { + arePropsDifferent = function (props1, props2) { + for (let k in props1) { + if (props1[k] !== props2[k]) { + return true; + } } - } - return hasDynamicPropList && Object.keys(props1).length !== Object.keys(props2).length; + return Object.keys(props1).length !== Object.keys(props2).length; + }; + } + else if (hasNoProp) { + arePropsDifferent = (_1, _2) => false; + } + else { + arePropsDifferent = function (props1, props2) { + for (let p of propList) { + if (props1[p] !== props2[p]) { + return true; + } + } + return false; + }; } - const arePropsDifferent = hasSlotsProp - ? (_1, _2) => true - : hasNoProp - ? (_1, _2) => false - : _arePropsDifferent; const updateAndRender = ComponentNode.prototype.updateAndRender; const initiateRender = ComponentNode.prototype.initiateRender; return (props, key, ctx, parent, C) => { @@ -5701,10 +5700,56 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration } } App.validateTarget = validateTarget; + App.version = version; async function mount(C, target, config = {}) { return new App(C, config).mount(target, config); - } - + } + + const mainEventHandler = (data, ev, currentTarget) => { + const { data: _data, modifiers } = filterOutModifiersFromData(data); + data = _data; + let stopped = false; + if (modifiers.length) { + let selfMode = false; + const isSelf = ev.target === currentTarget; + for (const mod of modifiers) { + switch (mod) { + case "self": + selfMode = true; + if (isSelf) { + continue; + } + else { + return stopped; + } + case "prevent": + if ((selfMode && isSelf) || !selfMode) + ev.preventDefault(); + continue; + case "stop": + if ((selfMode && isSelf) || !selfMode) + ev.stopPropagation(); + stopped = true; + continue; + } + } + } + // If handler is empty, the array slot 0 will also be empty, and data will not have the property 0 + // We check this rather than data[0] being truthy (or typeof function) so that it crashes + // as expected when there is a handler expression that evaluates to a falsy value + if (Object.hasOwnProperty.call(data, 0)) { + const handler = data[0]; + if (typeof handler !== "function") { + throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`); + } + let node = data[1] ? data[1].__owl__ : null; + if (node ? node.status === 1 /* MOUNTED */ : true) { + handler.call(node ? node.component : null, ev); + } + } + return stopped; + }; + function status(component) { switch (component.__owl__.status) { case 0 /* NEW */: @@ -5714,8 +5759,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration case 2 /* DESTROYED */: return "destroyed"; } - } - + } + // ----------------------------------------------------------------------------- // useRef // ----------------------------------------------------------------------------- @@ -5728,7 +5773,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration const refs = node.refs; return { get el() { - return refs[name] || null; + const el = refs[name]; + return (el === null || el === void 0 ? void 0 : el.ownerDocument.contains(el)) ? el : null; }, }; } @@ -5814,8 +5860,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration const boundHandler = handler.bind(node.component); onMounted(() => target.addEventListener(eventName, boundHandler, eventParams)); onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams)); - } - + } + config.shouldNormalizeDom = false; config.mainEventHandler = mainEventHandler; const blockDom = { @@ -5833,8 +5879,10 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration html, comment, }; - const __info__ = {}; - + const __info__ = { + version: App.version, + }; + TemplateSet.prototype._compileTemplate = function _compileTemplate(name, template) { return compile(template, { name, @@ -5842,50 +5890,49 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration translateFn: this.translateFn, translatableAttributes: this.translatableAttributes, }); - }; - - exports.App = App; - exports.Component = Component; - exports.EventBus = EventBus; - exports.OwlError = OwlError; - exports.__info__ = __info__; - exports.blockDom = blockDom; - exports.loadFile = loadFile; - exports.markRaw = markRaw; - exports.markup = markup; - exports.mount = mount; - exports.onError = onError; - exports.onMounted = onMounted; - exports.onPatched = onPatched; - exports.onRendered = onRendered; - exports.onWillDestroy = onWillDestroy; - exports.onWillPatch = onWillPatch; - exports.onWillRender = onWillRender; - exports.onWillStart = onWillStart; - exports.onWillUnmount = onWillUnmount; - exports.onWillUpdateProps = onWillUpdateProps; - exports.reactive = reactive; - exports.status = status; - exports.toRaw = toRaw; - exports.useChildSubEnv = useChildSubEnv; - exports.useComponent = useComponent; - exports.useEffect = useEffect; - exports.useEnv = useEnv; - exports.useExternalListener = useExternalListener; - exports.useRef = useRef; - exports.useState = useState; - exports.useSubEnv = useSubEnv; - exports.validate = validate; - exports.whenReady = whenReady; - exports.xml = xml; - - Object.defineProperty(exports, '__esModule', { value: true }); - - - __info__.version = '2.0.5'; - __info__.date = '2023-01-27T14:29:08.954Z'; - __info__.hash = 'ea5d2be'; - __info__.url = 'https://github.com/odoo/owl'; - - -})(this.owl = this.owl || {}); \ No newline at end of file + }; + + exports.App = App; + exports.Component = Component; + exports.EventBus = EventBus; + exports.OwlError = OwlError; + exports.__info__ = __info__; + exports.blockDom = blockDom; + exports.loadFile = loadFile; + exports.markRaw = markRaw; + exports.markup = markup; + exports.mount = mount; + exports.onError = onError; + exports.onMounted = onMounted; + exports.onPatched = onPatched; + exports.onRendered = onRendered; + exports.onWillDestroy = onWillDestroy; + exports.onWillPatch = onWillPatch; + exports.onWillRender = onWillRender; + exports.onWillStart = onWillStart; + exports.onWillUnmount = onWillUnmount; + exports.onWillUpdateProps = onWillUpdateProps; + exports.reactive = reactive; + exports.status = status; + exports.toRaw = toRaw; + exports.useChildSubEnv = useChildSubEnv; + exports.useComponent = useComponent; + exports.useEffect = useEffect; + exports.useEnv = useEnv; + exports.useExternalListener = useExternalListener; + exports.useRef = useRef; + exports.useState = useState; + exports.useSubEnv = useSubEnv; + exports.validate = validate; + exports.whenReady = whenReady; + exports.xml = xml; + + Object.defineProperty(exports, '__esModule', { value: true }); + + + __info__.date = '2023-03-13T09:55:06.791Z'; + __info__.hash = '8893e02'; + __info__.url = 'https://github.com/odoo/owl'; + + +})(this.owl = this.owl || {}); diff --git a/music_player/views/templates.xml b/music_player/views/templates.xml index 3638edf..0750c4e 100644 --- a/music_player/views/templates.xml +++ b/music_player/views/templates.xml @@ -1,8 +1,8 @@ - - - + + + \ No newline at end of file diff --git a/music_player/views/views.xml b/music_player/views/views.xml index f68ea66..5a85f6a 100644 --- a/music_player/views/views.xml +++ b/music_player/views/views.xml @@ -1,9 +1,9 @@ - + - + \ No newline at end of file