localstorage; parts becomes expose
This commit is contained in:
		@ -5,75 +5,6 @@ from django.core.urlresolvers import reverse
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __part_normalize(value, default):
 | 
			
		||||
    value = value if value else default
 | 
			
		||||
    return slugify(value.lower())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parts(cls, name = None, pattern = None):
 | 
			
		||||
    """
 | 
			
		||||
    the decorated class is a parts class, and contains part
 | 
			
		||||
    functions. Look `part` decorator doc for more info.
 | 
			
		||||
    """
 | 
			
		||||
    name = __part_normalize(name, cls.__name__)
 | 
			
		||||
    pattern = __part_normalize(pattern, cls.__name__)
 | 
			
		||||
 | 
			
		||||
    cls._parts = []
 | 
			
		||||
    for part in cls.__dict__.values():
 | 
			
		||||
        if not hasattr(part, 'is_part'):
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        part.name = name + '_' + part.name
 | 
			
		||||
        part.pattern = pattern + '/' + part.pattern
 | 
			
		||||
        part = url(part.pattern, name = part.name,
 | 
			
		||||
                   view = part, kwargs = {'cl': cls})
 | 
			
		||||
        cls._parts.append(part)
 | 
			
		||||
    return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def part(view, name = None, pattern = None):
 | 
			
		||||
    """
 | 
			
		||||
    A part function is a view that is used to retrieve data dynamically,
 | 
			
		||||
    e.g. from Javascript with XMLHttpRequest. A part function is a classmethod
 | 
			
		||||
    that returns a string and has the following signature:
 | 
			
		||||
 | 
			
		||||
        `part(cl, request, parent, *args, **kwargs)`
 | 
			
		||||
 | 
			
		||||
    When a section with parts is added to the website, the parts' urls
 | 
			
		||||
    are added to the website's one and make them available.
 | 
			
		||||
 | 
			
		||||
    A part function can have the following parameters:
 | 
			
		||||
    * name: part.name or part.__name__
 | 
			
		||||
    * pattern: part.pattern or part.__name__
 | 
			
		||||
 | 
			
		||||
    An extra method `url` is added to the part function to return the adequate
 | 
			
		||||
    url.
 | 
			
		||||
 | 
			
		||||
    Theses are combined with the containing parts class params such as:
 | 
			
		||||
    * name: parts.name + '_' + part.name
 | 
			
		||||
    * pattern: parts.pattern + '/' + part.pattern
 | 
			
		||||
 | 
			
		||||
    The parts class will have an attribute '_parts' as list of generated
 | 
			
		||||
    urls.
 | 
			
		||||
    """
 | 
			
		||||
    if hasattr(view, 'is_part'):
 | 
			
		||||
        return view
 | 
			
		||||
 | 
			
		||||
    def view_(request, as_str = False, cl = None, *args, **kwargs):
 | 
			
		||||
        v = view(cl, request, *args, **kwargs)
 | 
			
		||||
        if as_str:
 | 
			
		||||
            return v
 | 
			
		||||
        return HttpResponse(v)
 | 
			
		||||
 | 
			
		||||
    def url(*args, **kwargs):
 | 
			
		||||
        return reverse(view_.name, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    view_.name = __part_normalize(name, view.__name__)
 | 
			
		||||
    view_.pattern = __part_normalize(pattern, view.__name__)
 | 
			
		||||
    view_.is_part = True
 | 
			
		||||
    view_.url = url
 | 
			
		||||
    return view_
 | 
			
		||||
 | 
			
		||||
def template(name):
 | 
			
		||||
    """
 | 
			
		||||
    the decorated function returns a context that is used to
 | 
			
		||||
@ -82,16 +13,123 @@ def template(name):
 | 
			
		||||
    * template_name: name of the template to use
 | 
			
		||||
    * hide_empty: an empty context returns an empty string
 | 
			
		||||
    """
 | 
			
		||||
    def wrapper(func):
 | 
			
		||||
        def view_(cl, request, *args, **kwargs):
 | 
			
		||||
            context = func(cl, request, *args, **kwargs)
 | 
			
		||||
    def template_(func):
 | 
			
		||||
        def wrapper(request, *args, **kwargs):
 | 
			
		||||
            if kwargs.get('cl'):
 | 
			
		||||
                context = func(kwargs.pop('cl'), request, *args, **kwargs)
 | 
			
		||||
            else:
 | 
			
		||||
                context = func(request, *args, **kwargs)
 | 
			
		||||
            if not context:
 | 
			
		||||
                return ''
 | 
			
		||||
            context['embed'] = True
 | 
			
		||||
            return render_to_string(name, context, request=request)
 | 
			
		||||
        view_.__name__ = func.__name__
 | 
			
		||||
        return view_
 | 
			
		||||
    return wrapper
 | 
			
		||||
        return wrapper
 | 
			
		||||
    return template_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Exposure:
 | 
			
		||||
    """
 | 
			
		||||
    Define an exposure. Look at @expose decorator.
 | 
			
		||||
    """
 | 
			
		||||
    name = None
 | 
			
		||||
    """generated view name"""
 | 
			
		||||
    pattern = None
 | 
			
		||||
    """url pattern"""
 | 
			
		||||
    items = None
 | 
			
		||||
    """for classes: list of url objects for exposed methods"""
 | 
			
		||||
    template_name = None
 | 
			
		||||
    """
 | 
			
		||||
    for methods: exposed method return a context to be use with
 | 
			
		||||
    the given template. The view will be wrapped in @template
 | 
			
		||||
    """
 | 
			
		||||
    item = None
 | 
			
		||||
    """
 | 
			
		||||
    exposed item
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
    def url(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        reverse url for this exposure
 | 
			
		||||
        """
 | 
			
		||||
        return reverse(self.name, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def prefix(self, parent):
 | 
			
		||||
        """
 | 
			
		||||
        prefix exposure with the given parent
 | 
			
		||||
        """
 | 
			
		||||
        self.name = parent.name + '.' + self.name
 | 
			
		||||
        self.pattern = parent.pattern + '/' + self.pattern
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def expose(item):
 | 
			
		||||
    """
 | 
			
		||||
    Expose a class and its methods as views. This allows data to be
 | 
			
		||||
    retrieved dynamiccaly from client (e.g. with javascript).
 | 
			
		||||
 | 
			
		||||
    To expose a method of a class, you must expose the class, then the
 | 
			
		||||
    method.
 | 
			
		||||
 | 
			
		||||
    The exposed method has the following signature:
 | 
			
		||||
 | 
			
		||||
        `func(cl, request, parent, *args, **kwargs) -> str`
 | 
			
		||||
 | 
			
		||||
    Data related to the exposure are put in the `_exposure` attribute,
 | 
			
		||||
    as instance of Exposure.
 | 
			
		||||
 | 
			
		||||
    To add extra parameter, such as template_name, just update the correct
 | 
			
		||||
    field in func._exposure, that will be taken in account at the class
 | 
			
		||||
    decoration.
 | 
			
		||||
 | 
			
		||||
    The exposed method will be prefix'ed with it's parent class exposure.
 | 
			
		||||
 | 
			
		||||
    When adding views to a website, the exposure of their sections are
 | 
			
		||||
    added to the list of url.
 | 
			
		||||
    """
 | 
			
		||||
    def get_attr(attr, default):
 | 
			
		||||
        v = (hasattr(item, attr) and getattr(item, attr)) or default
 | 
			
		||||
        return slugify(v.lower())
 | 
			
		||||
 | 
			
		||||
    name = get_attr('name', item.__name__)
 | 
			
		||||
    pattern = get_attr('pattern', item.__name__)
 | 
			
		||||
 | 
			
		||||
    exp = Exposure(name = name, pattern = pattern, item = item)
 | 
			
		||||
 | 
			
		||||
    # expose a class container: set _exposure attribute
 | 
			
		||||
    if type(item) == type:
 | 
			
		||||
        exp.name = 'exp.' + exp.name
 | 
			
		||||
        exp.items = []
 | 
			
		||||
 | 
			
		||||
        for func in item.__dict__.values():
 | 
			
		||||
            if not hasattr(func, '_exposure'):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            sub = func._exposure
 | 
			
		||||
            sub.prefix(exp)
 | 
			
		||||
 | 
			
		||||
            # FIXME: template warping lose args
 | 
			
		||||
            if sub.template_name:
 | 
			
		||||
                sub.item = template(sub.template_name)(sub.item)
 | 
			
		||||
 | 
			
		||||
            func = url(sub.pattern, name = sub.name,
 | 
			
		||||
                       view = func, kwargs = {'cl': item})
 | 
			
		||||
            exp.items.append(func)
 | 
			
		||||
 | 
			
		||||
        item._exposure = exp;
 | 
			
		||||
        return item
 | 
			
		||||
    # expose a method: wrap it
 | 
			
		||||
    else:
 | 
			
		||||
        if hasattr(item, '_exposure'):
 | 
			
		||||
            del item._exposure
 | 
			
		||||
 | 
			
		||||
        def wrapper(request, as_str = False, *args, **kwargs):
 | 
			
		||||
            v = exp.item(request, *args, **kwargs)
 | 
			
		||||
            if as_str:
 | 
			
		||||
                return v
 | 
			
		||||
            return HttpResponse(v)
 | 
			
		||||
        wrapper._exposure = exp;
 | 
			
		||||
        return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ class Route:
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_view_name(cl, name):
 | 
			
		||||
        return name + '_' + cl.name
 | 
			
		||||
        return name + '.' + cl.name
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_url(cl, name, view, view_kwargs = None):
 | 
			
		||||
 | 
			
		||||
@ -35,8 +35,8 @@ class Website:
 | 
			
		||||
    ## components
 | 
			
		||||
    urls = []
 | 
			
		||||
    """list of urls generated thourgh registrations"""
 | 
			
		||||
    parts = []
 | 
			
		||||
    """list of registered parts (done through sections registration)"""
 | 
			
		||||
    exposures = []
 | 
			
		||||
    """list of registered exposures (done through sections registration)"""
 | 
			
		||||
    registry = {}
 | 
			
		||||
    """dict of registered models by their name"""
 | 
			
		||||
 | 
			
		||||
@ -45,8 +45,8 @@ class Website:
 | 
			
		||||
        * menus: a list of menus to add to the website
 | 
			
		||||
        """
 | 
			
		||||
        self.registry = {}
 | 
			
		||||
        self.parts = []
 | 
			
		||||
        self.urls = [ url(r'^parts/', include(self.parts)) ]
 | 
			
		||||
        self.exposures = []
 | 
			
		||||
        self.urls = [ url(r'^exp/', include(self.exposures)) ]
 | 
			
		||||
        self.menus = {}
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
@ -96,18 +96,18 @@ class Website:
 | 
			
		||||
        model._website = self
 | 
			
		||||
        return name
 | 
			
		||||
 | 
			
		||||
    def register_parts(self, sections):
 | 
			
		||||
    def register_exposures(self, sections):
 | 
			
		||||
        """
 | 
			
		||||
        Register parts that are used in the given sections.
 | 
			
		||||
        Register exposures that are used in the given sections.
 | 
			
		||||
        """
 | 
			
		||||
        if not hasattr(sections, '__iter__'):
 | 
			
		||||
            sections = [sections]
 | 
			
		||||
 | 
			
		||||
        for section in sections:
 | 
			
		||||
            if not hasattr(section, '_parts'):
 | 
			
		||||
            if not hasattr(section, '_exposure'):
 | 
			
		||||
                continue
 | 
			
		||||
            self.parts += [
 | 
			
		||||
                url for url in section._parts
 | 
			
		||||
            self.exposures += [
 | 
			
		||||
                url for url in section._exposure.items
 | 
			
		||||
                if url not in self.urls
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
@ -129,7 +129,7 @@ class Website:
 | 
			
		||||
            view_kwargs['menus'] = self.menus
 | 
			
		||||
 | 
			
		||||
        if sections:
 | 
			
		||||
            self.register_parts(sections)
 | 
			
		||||
            self.register_exposures(sections)
 | 
			
		||||
            view_kwargs['sections'] = sections
 | 
			
		||||
 | 
			
		||||
        view = view.as_view(
 | 
			
		||||
@ -172,7 +172,7 @@ class Website:
 | 
			
		||||
        elif menu.position in ('left', 'right'):
 | 
			
		||||
            menu.tag = 'side'
 | 
			
		||||
        self.menus[menu.position] = menu
 | 
			
		||||
        self.register_parts(menu.sections)
 | 
			
		||||
        self.register_exposures(menu.sections)
 | 
			
		||||
 | 
			
		||||
    def get_menu(self, position):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ import aircox.cms.decorators as decorators
 | 
			
		||||
import aircox.website.models as models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@decorators.parts
 | 
			
		||||
@decorators.expose
 | 
			
		||||
class Player(sections.Section):
 | 
			
		||||
    """
 | 
			
		||||
    Display a player that is cool.
 | 
			
		||||
@ -24,8 +24,7 @@ class Player(sections.Section):
 | 
			
		||||
    """
 | 
			
		||||
    #default_sounds
 | 
			
		||||
 | 
			
		||||
    @decorators.part
 | 
			
		||||
    @decorators.template('aircox/cms/list_item.html')
 | 
			
		||||
    @decorators.expose
 | 
			
		||||
    def on_air(cl, request):
 | 
			
		||||
        qs = programs.Diffusion.get(
 | 
			
		||||
            now = True,
 | 
			
		||||
@ -47,6 +46,8 @@ class Player(sections.Section):
 | 
			
		||||
            'item': post,
 | 
			
		||||
            'list': sections.List,
 | 
			
		||||
        }
 | 
			
		||||
    on_air._exposure.template_name = 'aircox/cms/list_item.html'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, *args, **kwargs):
 | 
			
		||||
        context = super().get_context_data(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
@ -5,50 +5,40 @@
 | 
			
		||||
 | 
			
		||||
{% block header %}
 | 
			
		||||
<style>
 | 
			
		||||
#player {
 | 
			
		||||
    background-color: #212121;
 | 
			
		||||
    color: #818181;
 | 
			
		||||
    border-radius: 0.2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.player-box {
 | 
			
		||||
    border-top-left-radius: 0.5em;
 | 
			
		||||
    border-top-right-radius: 0.5em;
 | 
			
		||||
    border: 1px #212121 solid;
 | 
			
		||||
    border-bottom: none;
 | 
			
		||||
    padding-top: 0.2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .player-box * {
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player-box h3 {
 | 
			
		||||
    .player-box h3, #player h2 {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .player-button {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        height: 2em;
 | 
			
		||||
        width: 2em;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        margin-right: 1em;
 | 
			
		||||
.player-button {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    height: 1.2em;
 | 
			
		||||
    width: 1.2em;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    margin-right: 0.4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .player-button img {
 | 
			
		||||
        height: inherit;
 | 
			
		||||
        box-shadow: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        .player-button img {
 | 
			
		||||
            height: inherit;
 | 
			
		||||
            box-shadow: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #player[play] .player-button img {
 | 
			
		||||
            margin-left: -100%;
 | 
			
		||||
        }
 | 
			
		||||
    #player[play] .player-button img {
 | 
			
		||||
        margin-left: -100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#player .on_air {
 | 
			
		||||
    background-color: #414141;
 | 
			
		||||
    box-shadow: inset 0 0 0.2em rgba(0, 0, 0, 0.5);
 | 
			
		||||
    padding: 0.2em;
 | 
			
		||||
    margin: 0.2em 0em;
 | 
			
		||||
}
 | 
			
		||||
@ -58,43 +48,27 @@
 | 
			
		||||
      display: inline;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #player .on_air .title:before {
 | 
			
		||||
      content: "{% trans "on air //" %}";
 | 
			
		||||
      color: red;
 | 
			
		||||
      margin: 0.2em;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #player .on_air a {
 | 
			
		||||
      float: right;
 | 
			
		||||
      color: black;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.playlists {}
 | 
			
		||||
.playlists {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        .playlists nav {
 | 
			
		||||
            font-size: 0.8em;
 | 
			
		||||
            border-bottom: 1px solid #007EDF;
 | 
			
		||||
        }
 | 
			
		||||
      .playlists ul:not([selected]) {
 | 
			
		||||
          display: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        .playlists nav a {
 | 
			
		||||
            padding: 0.2em;
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            .playlists nav > *[selected] {
 | 
			
		||||
                color: black;
 | 
			
		||||
                border-top-right-radius: 0.2em;
 | 
			
		||||
                background-color: rgba(0, 126, 223, 0.8);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        .playlists ul:not([selected]) {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
      .playlists nav a.close {
 | 
			
		||||
          float: right;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
.playlist {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    height: 15em;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .playlist .item > * {
 | 
			
		||||
@ -105,17 +79,12 @@
 | 
			
		||||
 | 
			
		||||
    .playlist .item .info {
 | 
			
		||||
        float: right;
 | 
			
		||||
        font-size: 0.8em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .playlist .item a {
 | 
			
		||||
        float: right;
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .playlist .item[selected] .title {
 | 
			
		||||
        color: #007EDF;
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
<div id="player">
 | 
			
		||||
    <li class='item' style="display: none;">
 | 
			
		||||
@ -135,7 +104,8 @@
 | 
			
		||||
            </audio>
 | 
			
		||||
 | 
			
		||||
            <span class="player-button" onclick="player.play()">
 | 
			
		||||
                <img src="{% static "aircox/website/player_button.png" %}">
 | 
			
		||||
                <img src="{% static "aircox/website/player_button.png" %}"
 | 
			
		||||
                     alt="{% trans "play audio" %}">
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <h3 class="title"></h3>
 | 
			
		||||
@ -147,17 +117,26 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="playlists">
 | 
			
		||||
        <nav></nav>
 | 
			
		||||
        <nav>
 | 
			
		||||
          <a onclick="player.select_playlist()" class="close"
 | 
			
		||||
             title="{% trans "close" %}">✖</a>
 | 
			
		||||
        </nav>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
function Playlist(id, name, items) {
 | 
			
		||||
// Create a Playlist:
 | 
			
		||||
// * name: name of the playlist, used for container id and storage
 | 
			
		||||
// * tab: text to put in the tab
 | 
			
		||||
// * items: list of items to append
 | 
			
		||||
// * store: store the playlist in localStorage
 | 
			
		||||
function Playlist(name, tab, items, store = false) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.store = store;
 | 
			
		||||
 | 
			
		||||
    var self = this;
 | 
			
		||||
    this.playlist = document.createElement('ul');
 | 
			
		||||
    this.playlist.setAttribute('id', id);
 | 
			
		||||
    this.playlist.setAttribute('id', 'playlist-' + name );
 | 
			
		||||
    this.playlist.className = 'playlist list';
 | 
			
		||||
 | 
			
		||||
    this.tab = document.createElement('a');
 | 
			
		||||
@ -166,12 +145,14 @@ function Playlist(id, name, items) {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
    }, true);
 | 
			
		||||
    this.tab.className = 'tab';
 | 
			
		||||
    this.tab.innerHTML = name;
 | 
			
		||||
    this.tab.innerHTML = tab;
 | 
			
		||||
 | 
			
		||||
    player.playlists.appendChild(this.playlist);
 | 
			
		||||
    player.playlists.querySelector('nav').appendChild(this.tab);
 | 
			
		||||
 | 
			
		||||
    self.items = [];
 | 
			
		||||
    this.items = [];
 | 
			
		||||
    if(store)
 | 
			
		||||
      this.load()
 | 
			
		||||
    if(items)
 | 
			
		||||
        this.add_list(items);
 | 
			
		||||
}
 | 
			
		||||
@ -188,7 +169,7 @@ Playlist.prototype = {
 | 
			
		||||
        if(container)
 | 
			
		||||
            container.appendChild(item);
 | 
			
		||||
        else
 | 
			
		||||
            self.playlist.appendChild(item);
 | 
			
		||||
            this.playlist.appendChild(item);
 | 
			
		||||
 | 
			
		||||
        info.item = item;
 | 
			
		||||
        item.info = info;
 | 
			
		||||
@ -208,6 +189,9 @@ Playlist.prototype = {
 | 
			
		||||
              event.preventDefault();
 | 
			
		||||
          }, true);
 | 
			
		||||
        this.items.push(info);
 | 
			
		||||
 | 
			
		||||
        if(this.store)
 | 
			
		||||
          this.save()
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /// Add a list of items (optimized)
 | 
			
		||||
@ -217,6 +201,26 @@ Playlist.prototype = {
 | 
			
		||||
            this.add(items[i], container);
 | 
			
		||||
        this.playlist.appendChild(container);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /// Save a playlist to local storage
 | 
			
		||||
    save: function() {
 | 
			
		||||
      pl = []
 | 
			
		||||
      for(var i = 0; i < this.items.length; i++)
 | 
			
		||||
        info = Object.assign({}, this.items[i])
 | 
			
		||||
        info.item = undefined;
 | 
			
		||||
        pl.push(info);
 | 
			
		||||
 | 
			
		||||
      localStorage.setItem('playlist.' + self.name,
 | 
			
		||||
                           JSON.stringify(pl))
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /// Load playlist from local storage
 | 
			
		||||
    load: function() {
 | 
			
		||||
      pl = localStorage.getItem('playlist.' + self.name)
 | 
			
		||||
      if(pl)
 | 
			
		||||
        this.add_list(JSON.parse(pl));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -232,8 +236,8 @@ player = {
 | 
			
		||||
        this.audio = this.player.querySelector('audio');
 | 
			
		||||
        this.playlists = this.player.querySelector('.playlists');
 | 
			
		||||
        this.live = new Playlist(
 | 
			
		||||
            'playlist-live',
 | 
			
		||||
            "{% trans "live" %}",
 | 
			
		||||
            'live',
 | 
			
		||||
            "☰ {% trans "live" %}",
 | 
			
		||||
            [ {% for sound in live_streams %}
 | 
			
		||||
                { title: "{{ sound.title }}",
 | 
			
		||||
                  url: "{{ sound.url }}",
 | 
			
		||||
@ -242,8 +246,7 @@ player = {
 | 
			
		||||
                }, {% endfor %} ]
 | 
			
		||||
        );
 | 
			
		||||
        this.recents = new Playlist(
 | 
			
		||||
            'playlist-recents',
 | 
			
		||||
            "{% trans "recents" %}",
 | 
			
		||||
            'recents', '☀ {% trans "recents" %}',
 | 
			
		||||
            [ {% for sound in last_sounds %}
 | 
			
		||||
                { title: "{{ sound.title }}",
 | 
			
		||||
                  url: "{{ sound.url }}",
 | 
			
		||||
@ -255,15 +258,18 @@ player = {
 | 
			
		||||
                  info: "{{ sound.related.duration|date:"i:s" }}",
 | 
			
		||||
                }, {% endfor %} ]
 | 
			
		||||
        );
 | 
			
		||||
        this.favorite = new Playlist(
 | 
			
		||||
            'favorite', '♥ {% trans "favorites" %}', null, true);
 | 
			
		||||
        this.playlist = new Playlist(
 | 
			
		||||
            'playlist', '★ {% trans "playlist" %}', null, true);
 | 
			
		||||
 | 
			
		||||
        this.select_playlist(this.recents);
 | 
			
		||||
        this.select(this.live.items[0], false)
 | 
			
		||||
        this.update_on_air();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /// update on air informations
 | 
			
		||||
    update_on_air: function() {
 | 
			
		||||
      part = Part('{% url "player_on_air" %}').get()
 | 
			
		||||
      part = Part('{% url "exp.player.on_air" %}').get()
 | 
			
		||||
          .select({
 | 
			
		||||
            title: '.title',
 | 
			
		||||
            url: ['.url', 'href'],
 | 
			
		||||
@ -332,8 +338,10 @@ player = {
 | 
			
		||||
        this.__unselect('.playlists .playlist[selected]');
 | 
			
		||||
 | 
			
		||||
        self.playlist = playlist
 | 
			
		||||
        playlist.playlist.setAttribute('selected', 'true');
 | 
			
		||||
        playlist.tab.setAttribute('selected', 'true');
 | 
			
		||||
        if(playlist) {
 | 
			
		||||
            playlist.playlist.setAttribute('selected', 'true');
 | 
			
		||||
            playlist.tab.setAttribute('selected', 'true');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user