diff --git a/README.md b/README.md deleted file mode 100644 index 5ffc634..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# Single_Page_Application_workshop \ No newline at end of file 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/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__/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/controllers.py b/music_player/controllers/controllers.py deleted file mode 100644 index 9f470b2..0000000 --- a/music_player/controllers/controllers.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -import json -from odoo import http -from odoo.http import Response -from odoo.modules.module import get_module_resource - -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') - - # A controller to play song from the audio - - @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 diff --git a/music_player/demo/demo.xml b/music_player/demo/demo.xml deleted file mode 100644 index f13bd4c..0000000 --- a/music_player/demo/demo.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - Akon-Be-With-You - Akon-Be-With-You.mp3 - - - Akon-Beautiful - Akon-Beautiful.mp3 - - - Akon-Birthmark - Akon-Birthmark.mp3 - - - Akon-Keep-You-Much-Longer - Akon-Keep-You-Much-Longer.mp3 - - - Akon-Sunny-Day - Akon-Sunny-Day.mp3 - - - - Temp-song-1 - Temp-song-1.mp3 - - - Temp-song-2 - Temp-song-2.mp3 - - - 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__/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__/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/player.py b/music_player/models/player.py deleted file mode 100644 index a7e7df7..0000000 --- a/music_player/models/player.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo import models, fields, api - - -class music_player(models.Model): - _name = 'music_player.music_player' - _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 - - def _compute_url(self): - for record in self: - record.url = record.get_base_url() + '/music/' + str(record.id) diff --git a/music_player/security/ir.model.access.csv b/music_player/security/ir.model.access.csv deleted file mode 100644 index 393f8ff..0000000 --- a/music_player/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_music_player_music_player,music_player.music_player,model_music_player_music_player,,1,1,1,1 \ No newline at end of file diff --git a/music_player/static/app.js b/music_player/static/app.js deleted file mode 100644 index 103f245..0000000 --- a/music_player/static/app.js +++ /dev/null @@ -1,129 +0,0 @@ -/** @odoo-module**/ - -const { Component, xml, mount, setup, useState } = owl; - -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; - } -} - -// class PlayList extends Component { -// static template = xml` -//
- -//
-// `; - -// } - -class MusicList extends Component { - static template = xml` -
- -

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) { - // 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 }; -} - -class Search extends Component { - static template = xml ` -
- - - -
- `; - setup() { - this.searchData = useState([]); - } - - async getMusic() { - 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. - this.searchData.push(newData); - } - - static components = { MusicList } -} - -class Root extends Component { // import from owl - // import from owl - static template = xml ` - -
- -
- `; - - static components = { Search }; -} - -window.onload = function() { - mount(Root, document.body); -}; \ No newline at end of file diff --git a/music_player/views/templates.xml b/music_player/views/templates.xml deleted file mode 100644 index 3638edf..0000000 --- a/music_player/views/templates.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/music_player/views/views.xml b/music_player/views/views.xml deleted file mode 100644 index f68ea66..0000000 --- a/music_player/views/views.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/music_player/__init__.py b/musicplayer/__init__.py similarity index 100% rename from music_player/__init__.py rename to musicplayer/__init__.py diff --git a/music_player/__manifest__.py b/musicplayer/__manifest__.py similarity index 84% rename from music_player/__manifest__.py rename to musicplayer/__manifest__.py index c004329..0280f6d 100644 --- a/music_player/__manifest__.py +++ b/musicplayer/__manifest__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- { - 'name': "musicPlayer", + 'name': "musicplayer", 'summary': """ Short (1 phrase/line) summary of the module's purpose, used as @@ -28,10 +28,15 @@ 'views/views.xml', 'views/templates.xml', ], + 'assets':{ + 'web.assets_backend':[ + 'musicplayer/app.js' + ] + }, # only loaded in demonstration mode 'demo': [ 'demo/demo.xml', ], - 'installable': True, - 'application': True, + 'installable':True, + 'application':True } diff --git a/musicplayer/__pycache__/__init__.cpython-38.pyc b/musicplayer/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..8070be7 Binary files /dev/null and b/musicplayer/__pycache__/__init__.cpython-38.pyc differ diff --git a/music_player/controllers/__init__.py b/musicplayer/controllers/__init__.py similarity index 100% rename from music_player/controllers/__init__.py rename to musicplayer/controllers/__init__.py diff --git a/musicplayer/controllers/__pycache__/__init__.cpython-38.pyc b/musicplayer/controllers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..b13e4bd Binary files /dev/null and b/musicplayer/controllers/__pycache__/__init__.cpython-38.pyc differ diff --git a/musicplayer/controllers/__pycache__/controllers.cpython-38.pyc b/musicplayer/controllers/__pycache__/controllers.cpython-38.pyc new file mode 100644 index 0000000..0552141 Binary files /dev/null and b/musicplayer/controllers/__pycache__/controllers.cpython-38.pyc differ diff --git a/musicplayer/controllers/controllers.py b/musicplayer/controllers/controllers.py new file mode 100644 index 0000000..c55dc61 --- /dev/null +++ b/musicplayer/controllers/controllers.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +import json + +from odoo.http import Response +from odoo import http +from odoo.modules.module import get_module_resource + +class Musicplayer(http.Controller): + @http.route('/music', auth='public') + def index(self, **kw): + return http.request.render('musicplayer.music_template') + + @http.route('/music/search', auth='public') + def search(self, **kw): + song_name = kw.get('song_name') + musics = http.request.env['musicplayer.musicplayer'].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('/musicplayer/musicplayer/objects/', auth='public') +# def object(self, obj, **kw): +# return http.request.render('musicplayer.object', { +# 'object': obj +# }) + # @http.route('/music/fetch', type='http', auth='public', methods=['GET']) + # def find(self, **kw): + # album = kw.get('album_name') + # albums = http.request.env['music.album'].search_read([('name', 'ilike', album)], fields=['name', 'player_ids']) + # player_ids = [player_id for album in albums for player_id in album['player_ids']] + # musics = http.request.env['music.player'].search_read([('id', 'in', player_ids)],fields={"name", "url"}) + # print(musics) + + # if not albums: + # albums = "Album not Found" + + # return Response(json.dumps({'result': musics}), content_type='application/json') + @http.route('/music/', type="http", auth='public') + def load(self, music, **kw): + music_file_path = get_module_resource('musicplayer', 'static/songs', music.filename) + file = open(music_file_path, 'rb').read() + return file \ No newline at end of file diff --git a/musicplayer/demo/demo.xml b/musicplayer/demo/demo.xml new file mode 100644 index 0000000..90f73ab --- /dev/null +++ b/musicplayer/demo/demo.xml @@ -0,0 +1,23 @@ + + + + Akon-Be-With-You + Akon-Be-With-You.mp3 + + + Akon-Beautiful + Akon-Beautiful.mp3 + + + Akon-Birthmark + Akon-Birthmark.mp3 + + + Akon-Keep-You-Much-Longer + Akon-Keep-You-Much-Longer.mp3 + + + Akon-Sunny-Day + Akon-Sunny-Day.mp3 + + diff --git a/music_player/models/__init__.py b/musicplayer/models/__init__.py similarity index 100% rename from music_player/models/__init__.py rename to musicplayer/models/__init__.py diff --git a/musicplayer/models/__pycache__/__init__.cpython-38.pyc b/musicplayer/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..feaf291 Binary files /dev/null and b/musicplayer/models/__pycache__/__init__.cpython-38.pyc differ diff --git a/musicplayer/models/__pycache__/models.cpython-38.pyc b/musicplayer/models/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000..6b5e1fb Binary files /dev/null and b/musicplayer/models/__pycache__/models.cpython-38.pyc differ diff --git a/musicplayer/models/__pycache__/player.cpython-38.pyc b/musicplayer/models/__pycache__/player.cpython-38.pyc new file mode 100644 index 0000000..925a206 Binary files /dev/null and b/musicplayer/models/__pycache__/player.cpython-38.pyc differ diff --git a/musicplayer/models/player.py b/musicplayer/models/player.py new file mode 100644 index 0000000..4e38069 --- /dev/null +++ b/musicplayer/models/player.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api + + +class musicplayer(models.Model): + _name = 'musicplayer.musicplayer' + _description = 'musicplayer.musicplayer' + + name = fields.Char('Song Name') + filename = fields.Char("File Name") + url = fields.Char(compute="_compute_url") + + def _compute_url(self): + for records in self: + records.url = records.get_base_url() + '/music/' + str(records.id) +# value = fields.Integer() +# value2 = fields.Float(compute="_value_pc", store=True) +# description = fields.Text() +# +# @api.depends('value') +# def _value_pc(self): +# for record in self: +# record.value2 = float(record.value) / 100 diff --git a/musicplayer/security/ir.model.access.csv b/musicplayer/security/ir.model.access.csv new file mode 100644 index 0000000..20449e2 --- /dev/null +++ b/musicplayer/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_musicplayer_musicplayer,musicplayer.musicplayer,model_musicplayer_musicplayer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/musicplayer/static/app.js b/musicplayer/static/app.js new file mode 100644 index 0000000..e5e9fad --- /dev/null +++ b/musicplayer/static/app.js @@ -0,0 +1,136 @@ +/** @odoo-module**/ + +const{ Component , xml, mount,useState} = owl; +let audio="" +class Player extends Component{ + static template = xml` +
+

Song Title

+
+ + + +
+
`; + playThisSong() { + if (!audio) { + return; + } + audio.play(); + } + pauseThisSong() { + console.log("pause") + if (!audio) { + return; + } + audio.pause(); + } + stopThisSong() { + if (!audio) { + return; + } + audio.pause(); + audio.currentTime = 0; + } +} + +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) { + 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(); + } + static props = ['playlist']; +} +class MusicList extends Component{ + static template = xml` +
+ +

List of Songs

+ +

+ + +
+
+ +
+ `; + + addSongToPlaylist(ev) { + const selectedSongUrl = ev.target.getAttribute('value'); + const selectedSong = this.props.searchData[0].find(song => song.url === selectedSongUrl); + this.props.addToPlayList(selectedSong); + } + playSong(ev) { + + 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}; +} +class Search extends Component{ + static template = xml` +
+ + + +
+ `; + setup(){ + this.searchData = useState([]); + } + async getMusic(){ + const findSong = document.getElementById("searchSong").value; + const response = await fetch(`/music/search?song_name=${findSong}`); + const {result : newData} = await response.json(); + this.searchData.pop(newData) + this.searchData.push(newData) + } + static components = {MusicList}; +} + +class Root extends Component{ + static template = xml` +
+ + +
+ `; + setup(){ + this.playData = useState([]); + } + addToPlayList(song){ + this.playData.push(song) + } + static components = {Search,PlayList}; +} +window.onload = function() +{ + mount(Root, document.body) +}; \ No newline at end of file diff --git a/music_player/static/owl.js b/musicplayer/static/owl.js similarity index 94% rename from music_player/static/owl.js rename to musicplayer/static/owl.js index 7e024b6..44f5ef7 100644 --- a/music_player/static/owl.js +++ b/musicplayer/static/owl.js @@ -18,7 +18,7 @@ // will go through this function, giving it the data registered in the block // and the event mainEventHandler: (data, ev, currentTarget) => { - if (typeof data === "function") { + if (typeof data === "function") { data(ev); } else if (Array.isArray(data)) { @@ -280,32 +280,6 @@ } } } - 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]; @@ -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; } @@ -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 @@ -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 // --------------------------------------------------------------------------- @@ -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) { @@ -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,16 +3084,15 @@ withKey, prepareList, setContextValue, - multiRefSetter, shallowEqual, toNumber, validateProps, LazyValue, safeOutput, - bind, createCatcher, markRaw, OwlError, + makeRefWrapper, }; const bdom = { text, createBlock, list, multi, html, toggler, comment }; @@ -3527,6 +3519,7 @@ 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})`; @@ -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 }; } @@ -5457,50 +5480,8 @@ 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 @@ -5577,12 +5558,15 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration }; 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 */: @@ -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; }, }; } @@ -5833,7 +5879,9 @@ 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, { @@ -5882,10 +5930,9 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration Object.defineProperty(exports, '__esModule', { value: true }); - __info__.version = '2.0.5'; - __info__.date = '2023-01-27T14:29:08.954Z'; - __info__.hash = 'ea5d2be'; + __info__.date = '2023-03-13T09:55:06.791Z'; + __info__.hash = '8893e02'; __info__.url = 'https://github.com/odoo/owl'; -})(this.owl = this.owl || {}); \ No newline at end of file +})(this.owl = this.owl || {}); diff --git a/music_player/static/songs/Akon-Be-With-You.mp3 b/musicplayer/static/songs/Akon-Be-With-You.mp3 similarity index 100% rename from music_player/static/songs/Akon-Be-With-You.mp3 rename to musicplayer/static/songs/Akon-Be-With-You.mp3 diff --git a/music_player/static/songs/Akon-Beautiful.mp3 b/musicplayer/static/songs/Akon-Beautiful.mp3 similarity index 100% rename from music_player/static/songs/Akon-Beautiful.mp3 rename to musicplayer/static/songs/Akon-Beautiful.mp3 diff --git a/music_player/static/songs/Akon-Birthmark.mp3 b/musicplayer/static/songs/Akon-Birthmark.mp3 similarity index 100% rename from music_player/static/songs/Akon-Birthmark.mp3 rename to musicplayer/static/songs/Akon-Birthmark.mp3 diff --git a/music_player/static/songs/Akon-Keep-You-Much-Longer.mp3 b/musicplayer/static/songs/Akon-Keep-You-Much-Longer.mp3 similarity index 100% rename from music_player/static/songs/Akon-Keep-You-Much-Longer.mp3 rename to musicplayer/static/songs/Akon-Keep-You-Much-Longer.mp3 diff --git a/music_player/static/songs/Akon-Right-Now.mp3 b/musicplayer/static/songs/Akon-Right-Now.mp3 similarity index 100% rename from music_player/static/songs/Akon-Right-Now.mp3 rename to musicplayer/static/songs/Akon-Right-Now.mp3 diff --git a/music_player/static/songs/Akon-Sunny-Day.mp3 b/musicplayer/static/songs/Akon-Sunny-Day.mp3 similarity index 100% rename from music_player/static/songs/Akon-Sunny-Day.mp3 rename to musicplayer/static/songs/Akon-Sunny-Day.mp3 diff --git a/musicplayer/views/templates.xml b/musicplayer/views/templates.xml new file mode 100644 index 0000000..c7d41df --- /dev/null +++ b/musicplayer/views/templates.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/musicplayer/views/views.xml b/musicplayer/views/views.xml new file mode 100644 index 0000000..d65f659 --- /dev/null +++ b/musicplayer/views/views.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file