diff options
| author | Joris | 2017-02-27 15:22:35 +0100 | 
|---|---|---|
| committer | Joris | 2017-02-27 15:22:35 +0100 | 
| commit | 154b398c144b51c7609c9141b8dc9aaa80fae04f (patch) | |
| tree | 756d5cbe976a29aa79986a78c6e87df2cb475270 /src | |
| parent | 0567fff028f2f8db6fe5034ce92d52f62469ae1f (diff) | |
Filter when the user click on a theme / genre / program on book detail
Diffstat (limited to 'src')
17 files changed, 149 insertions, 86 deletions
| diff --git a/src/main/scala/reading/Route.scala b/src/main/scala/reading/Route.scala index f098468..89c34cf 100644 --- a/src/main/scala/reading/Route.scala +++ b/src/main/scala/reading/Route.scala @@ -11,7 +11,7 @@ import reading.models.{ Filter, FilterKind, Book, Books => BooksModel }  sealed trait Route  object Route { -  case class Books(filters: Seq[Filter] = Nil, detail: Option[Book] = None) extends Route +  case class Books(filters: Set[Filter] = Set(), detail: Option[Book] = None) extends Route    val current: Var[Route] = Var(parse(window.location.hash)) @@ -35,7 +35,7 @@ object Route {              } yield filter            case _ =>              None -        } +        }.toSet          val detail = params.collectFirst {            case Param("detail", title) => BooksModel().find(_.title == title)          }.flatten @@ -56,7 +56,7 @@ object Route {    def url(route: Route): String = {      val hash = route match {        case Books(filters, detail) => { -        val filterParams = filters.map(filter => (filter.kind.toString, filter.nonFormattedName)) +        val filterParams = filters.toSeq.map(filter => (filter.kind.toString, filter.nonFormattedName))          val detailParams = detail.map(book => ("detail", book.title))          s"/books${Param.format(filterParams ++ detailParams)}"        } @@ -70,9 +70,8 @@ object Route {      current() = route    } -  def push(route: Route): Unit = { +  def push(route: Route): Unit =      window.history.pushState(null, "", url(route)); -  }  }  object Param { diff --git a/src/main/scala/reading/component/Index.scala b/src/main/scala/reading/component/Index.scala index 0105150..d6d1b6f 100644 --- a/src/main/scala/reading/component/Index.scala +++ b/src/main/scala/reading/component/Index.scala @@ -10,8 +10,8 @@ import reading.component.style.{ Index => IndexStyle }  import reading.models.{ Book, Books, Filter }  object Index { -  def apply(initialFilters: Seq[Filter], initialDetail: Option[Book])(implicit ctx: Ctx.Owner): Frag = { -    val filters: Var[Seq[Filter]] = Var(initialFilters) +  def apply(initialFilters: Set[Filter], initialDetail: Option[Book])(implicit ctx: Ctx.Owner): Frag = { +    val filters: Var[Set[Filter]] = Var(initialFilters)      val books: Rx[Seq[Book]] = Rx(Filter.add(Books(), filters()))      val search: Var[String] = Var("")      val showFiltersMenu: Var[Boolean] = Var(false) diff --git a/src/main/scala/reading/component/index/BookDetail.scala b/src/main/scala/reading/component/index/BookDetail.scala index f532c02..e97a154 100644 --- a/src/main/scala/reading/component/index/BookDetail.scala +++ b/src/main/scala/reading/component/index/BookDetail.scala @@ -1,27 +1,32 @@  package reading.component.index -import scala.util.Random - +import rx._  import scalacss.Defaults._  import scalacss.ScalatagsCss._  import scalatags.JsDom.all._  import reading.component.index.style.{ BookDetail => BookStyle }  import reading.component.widget.AnimateMethod -import reading.models.{ Book, Program, Filter } +import reading.models.{ Filter, Book, Program, Grade, Theme, GroupedTheme }  import reading.Route +import reading.utils.RxUtils._  object BookDetail { -  val componentId = s"books${Random.nextInt}" +  val componentId = s"books-detail"    def apply( -    filters: Seq[Filter], +    filters: Var[Set[Filter]], +    detail: Var[Option[Book]], +    search: Var[String],      book: Book,      parentId: String,      onClose: => Unit +  )( +    implicit +    ctx: Ctx.Owner    ): Frag = {      val titleParts = if (book.parts > 1) s", ${book.parts} volumes" else "" -    val grades = book.programs.map(Program.grade(_)).distinct.sorted +    val grades = book.programs.map(Grade.from(_)).distinct.sorted      AnimateMethod.fadeIn(componentId) @@ -55,23 +60,33 @@ object BookDetail {              BookStyle.definitions,              grades.map { grade => -              val programs = book.programs.filter(p => Program.grade(p) == grade).sorted +              val programs = book.programs.filter(p => Grade.from(p) == grade).sorted                val pp = grade.prettyPrint -              definition(pp, pp, programs.map(p => s"« ${p.prettyPrint} »")) +              val programFilters = programs.map { program => +                (Filter(program), filters.map(addProgram(_, program))) +              } +              definition(filters, detail, search, pp, pp, programFilters, p => s"« $p »")              },              if (book.themes.nonEmpty) { -              definition("thème", "thèmes", book.themes.sorted.map(_.prettyPrint)) +              val themeFilters = book.themes.sorted.map { theme => +                (Filter(theme), filters.map(addTheme(_, theme))) +              } +              definition(filters, detail, search, "thème", "thèmes", themeFilters)              },              if (book.genres.nonEmpty) { -              definition("genre", "genres", book.genres.sorted.map(_.prettyPrint)) +              val bookFilters = book.genres.sorted.map(Filter(_)).map(b => (b, filters.map(_ + b))) +              definition(filters, detail, search, "genre", "genres", bookFilters)              }, -            definition("niveau", "niveaux", Seq(book.level.prettyPrint)) +            { +              val levelFilters = Seq(Filter(book.level)).map(b => (b, filters.map(_ + b))) +              definition(filters, detail, search, "niveau", "niveaux", levelFilters) +            }            ),            a(              BookStyle.close,              onclick := (() => onClose), -            href := Route.url(Route.Books(filters)), +            href := Rx(Route.url(Route.Books(filters()))),              "Fermer"            )          ) @@ -79,11 +94,48 @@ object BookDetail {      )    } -  private def definition(key: String, pluralKey: String, values: Seq[String]): Seq[Frag] = { -    val term = if (values.length > 1) pluralKey else key +  private def definition( +    filters: Var[Set[Filter]], +    detail: Var[Option[Book]], +    search: Var[String], +    term: String, +    pluralTerm: String, +    definitionFilters: Seq[(Filter, Rx[Set[Filter]])], +    format: String => String = _.capitalize +  )( +    implicit +    ctx: Ctx.Owner +  ): Seq[Frag] = { +    val sTerm = if (definitionFilters.length > 1) pluralTerm else term +      Seq( -      dt(BookStyle.definitionTerm, s"${term.capitalize} :"), -      dd(BookStyle.definitionDescription, values.mkString(", ")) +      dt(BookStyle.definitionTerm, s"${sTerm.capitalize} :"), +      dd( +        BookStyle.definitionDescription, +        definitionFilters.map { +          case (filter, newFilters) => +            a( +              BookStyle.definitionFilter, +              href := Rx(Route.url(Route.Books(newFilters()))), +              onclick := (() => FilterUtils.set(filters, detail, search, newFilters.now)), +              format(filter.name) +            ) +        } +      )      )    } + +  private def addProgram(filters: Set[Filter], program: Program): Set[Filter] = { +    val grade = Grade.from(program) +    val otherGrades = Grade.values.filter(_ != grade).map(Filter(_)) +    val otherPrograms = Program.values.filter(Grade.from(_) != grade).map(Filter(_)) +    filters -- otherGrades -- otherPrograms + Filter(grade) + Filter(program) +  } + +  private def addTheme(filters: Set[Filter], theme: Theme): Set[Filter] = { +    val groupedTheme = GroupedTheme.from(theme) +    val otherGroupedThemes = GroupedTheme.values.filter(_ != groupedTheme).map(Filter(_)) +    val otherThemes = Theme.values.filter(GroupedTheme.from(_) != groupedTheme).map(Filter(_)) +    filters -- otherGroupedThemes -- otherThemes + Filter(groupedTheme) + Filter(theme) +  }  } diff --git a/src/main/scala/reading/component/index/Books.scala b/src/main/scala/reading/component/index/Books.scala index f15e2dc..14aac51 100644 --- a/src/main/scala/reading/component/index/Books.scala +++ b/src/main/scala/reading/component/index/Books.scala @@ -18,7 +18,7 @@ object Books {    def apply(      books: Rx[Seq[Book]], -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      showFiltersMenu: Var[Boolean] @@ -75,7 +75,10 @@ object Books {        Rx {          detail() match {            case Some(book) => -            BookDetail(filters.now, book, componentId, onClose = closeDetail(filters, detail)) +            BookDetail(filters, detail, search, book, componentId, onClose = { +              closeDetail(filters, detail) +              Route.push(Route.Books(filters.now, None)) +            })            case None =>              span("")          } @@ -83,10 +86,9 @@ object Books {      )    } -  def closeDetail(filters: Var[Seq[Filter]], detail: Var[Option[Book]]): Unit = +  def closeDetail(filters: Var[Set[Filter]], detail: Var[Option[Book]]): Unit =      AnimateMethod.fadeOut(BookDetail.componentId, onEnd = {        detail() = None -      Route.push(Route.Books(filters.now, None))        AnimateMethod.fadeIn(componentId)      })  } diff --git a/src/main/scala/reading/component/index/FilterUtils.scala b/src/main/scala/reading/component/index/FilterUtils.scala index 89f993a..c019513 100644 --- a/src/main/scala/reading/component/index/FilterUtils.scala +++ b/src/main/scala/reading/component/index/FilterUtils.scala @@ -7,7 +7,7 @@ import reading.Route  object FilterUtils {    def remove( -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      filter: Filter @@ -20,23 +20,22 @@ object FilterUtils {    }    def removeAll( -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String]    ): Unit = { -    filters() = Nil +    filters() = Set()      if (detail.now.nonEmpty) Books.closeDetail(filters, detail)      search() = "" -    Route.push(Route.Books(Nil)) +    Route.push(Route.Books())    } -  def add( -    filters: Var[Seq[Filter]], +  def set( +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String], -    filter: Filter +    newFilters: Set[Filter]    ): Unit = { -    val newFilters = filter +: filters.now      filters() = newFilters      if (detail.now.nonEmpty) Books.closeDetail(filters, detail)      search() = "" diff --git a/src/main/scala/reading/component/index/Filters.scala b/src/main/scala/reading/component/index/Filters.scala index 935e501..c300190 100644 --- a/src/main/scala/reading/component/index/Filters.scala +++ b/src/main/scala/reading/component/index/Filters.scala @@ -15,7 +15,7 @@ import reading.utils.RxUtils._  object Filters {    def apply( -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      showFiltersMenu: Var[Boolean] @@ -23,7 +23,7 @@ object Filters {      implicit      ctx: Ctx.Owner    ): Frag = { -    val filtersCount: Rx[Int] = Rx(filters().length) +    val filtersCount: Rx[Int] = Rx(filters().size)      div(        FiltersStyle.render, @@ -51,7 +51,7 @@ object Filters {                  "Effacer les filtres"                ), -              filters().sortBy(_.name).map { filter => +              filters().toSeq.sortBy(_.name).map { filter =>                  a(                    FiltersStyle.filter,                    onclick := (() => FilterUtils.remove(filters, detail, search, filter)), diff --git a/src/main/scala/reading/component/index/Header.scala b/src/main/scala/reading/component/index/Header.scala index 0809b0c..2d9504e 100644 --- a/src/main/scala/reading/component/index/Header.scala +++ b/src/main/scala/reading/component/index/Header.scala @@ -13,7 +13,7 @@ import reading.utils.RxUtils._  object Header {    def apply(      books: Rx[Seq[Book]], -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      showFiltersMenu: Var[Boolean] diff --git a/src/main/scala/reading/component/index/Menu.scala b/src/main/scala/reading/component/index/Menu.scala index 0ea6ca4..ec6c21f 100644 --- a/src/main/scala/reading/component/index/Menu.scala +++ b/src/main/scala/reading/component/index/Menu.scala @@ -14,7 +14,7 @@ import reading.utils.RxUtils._  object Menu {    def apply(      books: Rx[Seq[Book]], -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      showFiltersMenu: Var[Boolean] @@ -27,14 +27,14 @@ object Menu {        Rx(if (showFiltersMenu()) MenuStyle.show else MenuStyle.empty),        MenuStyle.menu, -      Rx(header(showFiltersMenu, filters().length)), +      Rx(header(showFiltersMenu, filters().size)),        div(          MenuStyle.groups,          Rx {            filters().find(_.kind == FilterKind.Grade) match {              case Some(grade) => -              val programs = Program.values.filter(p => Program.grade(p).toString() == grade.nonFormattedName).sorted +              val programs = Program.values.filter(p => Grade.from(p).toString() == grade.nonFormattedName).sorted                group(books, filters, detail, search, grade.name, grade.name, programs.map(Filter.apply(_)), Some(grade))              case None =>                group(books, filters, detail, search, "classe", "classes", Grade.values.sorted.map(Filter.apply(_))) @@ -43,7 +43,7 @@ object Menu {          Rx {            filters().find(_.kind == FilterKind.GroupedTheme) match {              case Some(groupedTheme) => -              val themes = Theme.values.filter(t => Theme.grouped(t).toString() == groupedTheme.nonFormattedName).sorted +              val themes = Theme.values.filter(t => GroupedTheme.from(t).toString() == groupedTheme.nonFormattedName).sorted                group(books, filters, detail, search, groupedTheme.name, groupedTheme.name, themes.map(Filter.apply(_)), Some(groupedTheme))              case None =>                group(books, filters, detail, search, "thème", "thèmes", GroupedTheme.values.sorted.map(Filter.apply(_))) @@ -66,7 +66,7 @@ object Menu {    def group(      books: Rx[Seq[Book]], -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      name: String, @@ -100,7 +100,7 @@ object Menu {              if (parentFilter.isDefined) MenuStyle.activeFilter else "",              if (filtersCount() > 1) pluralName else name,              Rx { -              val count = filters().filter(f => groupFilters.exists(f == _)).length +              val count = filters().filter(f => groupFilters.exists(f == _)).size                if (count > 0) span(MenuStyle.filterTitleCount, count) else span("")              }            ), @@ -109,7 +109,7 @@ object Menu {                case (filter, count) => {                  val isActive = Filter.contains(filters(), filter)                  val route = Route.Books( -                  filters = if (isActive) Filter.remove(filters(), filter) else filter +: filters() +                  filters = if (isActive) Filter.remove(filters(), filter) else filters() + filter                  )                  a( @@ -120,7 +120,7 @@ object Menu {                      if (isActive)                        FilterUtils.remove(filters, detail, search, filter)                      else -                      FilterUtils.add(filters, detail, search, filter)), +                      FilterUtils.set(filters, detail, search, filters.now + filter)),                    span(                      span(filter.name.capitalize),                      span(MenuStyle.filterCount, count) @@ -135,7 +135,7 @@ object Menu {    def footer(      books: Rx[Seq[Book]], -    filters: Var[Seq[Filter]], +    filters: Var[Set[Filter]],      detail: Var[Option[Book]],      search: Var[String],      showFiltersMenu: Var[Boolean] diff --git a/src/main/scala/reading/component/index/style/BookDetail.scala b/src/main/scala/reading/component/index/style/BookDetail.scala index 2ce0632..eaad862 100644 --- a/src/main/scala/reading/component/index/style/BookDetail.scala +++ b/src/main/scala/reading/component/index/style/BookDetail.scala @@ -71,6 +71,15 @@ object BookDetail extends StyleSheet.Inline {      lineHeight(1.4.em)    ) +  val definitionFilter = style( +    &.hover(color(C.stiletto.value)), +    &.not(_.lastChild).after( +      content := "\",\"", +      marginRight(5.px), +      &.hover(color(C.black.value)) +    ) +  ) +    val close = style(      Button.simple,      marginBottom(1.em) diff --git a/src/main/scala/reading/component/index/style/Books.scala b/src/main/scala/reading/component/index/style/Books.scala index a1f0374..808b937 100644 --- a/src/main/scala/reading/component/index/style/Books.scala +++ b/src/main/scala/reading/component/index/style/Books.scala @@ -21,7 +21,8 @@ object Books extends StyleSheet.Inline {    )    val listParent = style( -    overflowY.scroll +    overflowY.scroll, +    paddingTop(15.px)    )    val list = style( diff --git a/src/main/scala/reading/component/index/style/Header.scala b/src/main/scala/reading/component/index/style/Header.scala index 10ce059..a871e9c 100644 --- a/src/main/scala/reading/component/index/style/Header.scala +++ b/src/main/scala/reading/component/index/style/Header.scala @@ -9,7 +9,8 @@ object Header extends StyleSheet.Inline {    import dsl._    val header = style( -    margin(30.px, 0.px) +    marginTop(30.px), +    marginBottom(20.px)    )    val searchAndCount = style( diff --git a/src/main/scala/reading/models/Filter.scala b/src/main/scala/reading/models/Filter.scala index 7ec6340..e4c8b93 100644 --- a/src/main/scala/reading/models/Filter.scala +++ b/src/main/scala/reading/models/Filter.scala @@ -32,10 +32,10 @@ object Filter {        case FilterKind.Grade => Grade.withNameOption(nonFormattedName).map(apply[Grade])      } -  def contains(filters: Seq[Filter], filter: Filter): Boolean = +  def contains(filters: Set[Filter], filter: Filter): Boolean =      filters.find(_ == filter).nonEmpty -  def remove(fs: Seq[Filter], rf: Filter): Seq[Filter] = +  def remove(fs: Set[Filter], rf: Filter): Set[Filter] =      fs.filterNot { f =>        (f == rf          || rf.kind == FilterKind.Grade && f.kind == FilterKind.Program @@ -56,7 +56,7 @@ object Filter {        .map(f => (f, Books().filter(f.filter)))        .toMap -  def add(books: Seq[Book], filters: Seq[Filter]): Seq[Book] = +  def add(books: Seq[Book], filters: Set[Filter]): Seq[Book] =      filters.foldLeft(books)(add)    def add(books: Seq[Book], filter: Filter): Seq[Book] = diff --git a/src/main/scala/reading/models/FilterFactory.scala b/src/main/scala/reading/models/FilterFactory.scala index d900af5..d8d7151 100644 --- a/src/main/scala/reading/models/FilterFactory.scala +++ b/src/main/scala/reading/models/FilterFactory.scala @@ -15,10 +15,10 @@ object FilterFactory {        }    } -  implicit object GroupedTheme extends FilterFactory[GroupedTheme] { +  implicit object GroupedThemeFilter extends FilterFactory[GroupedTheme] {      def create(groupedTheme: GroupedTheme): Filter =        new Filter { -        def filter(book: Book): Boolean = book.themes.map(Theme.grouped).contains(groupedTheme) +        def filter(book: Book): Boolean = book.themes.map(GroupedTheme.from).contains(groupedTheme)          val kind: FilterKind = FilterKind.GroupedTheme          val nonFormattedName: String = groupedTheme.toString()          val name: String = groupedTheme.prettyPrint() @@ -58,7 +58,7 @@ object FilterFactory {    implicit object GradeFilter extends FilterFactory[Grade] {      def create(grade: Grade): Filter =        new Filter { -        def filter(book: Book): Boolean = book.programs.map(Program.grade).contains(grade) +        def filter(book: Book): Boolean = book.programs.map(Grade.from).contains(grade)          val kind: FilterKind = FilterKind.Grade          val nonFormattedName: String = grade.toString()          val name: String = grade.prettyPrint() diff --git a/src/main/scala/reading/models/Grade.scala b/src/main/scala/reading/models/Grade.scala index f54211d..c711e8b 100644 --- a/src/main/scala/reading/models/Grade.scala +++ b/src/main/scala/reading/models/Grade.scala @@ -24,4 +24,15 @@ object Grade extends Enum[Grade] {    case object Cinquieme extends Grade    case object Quatrieme extends Grade    case object Troisieme extends Grade + +  def from(program: Program): Grade = { +    import Program._ + +    program match { +      case Monstre | RecitAventure | CreationPoetique | Resister => Sixieme +      case VoyageEtAventure | Autrui | UniversNouveaux | Heros | HommeEtNature => Cinquieme +      case DireAmour | Valeurs | Reel | Informer | Ville => Quatrieme +      case SeRaconter | TraversSociete | VisionsPoetiques | Agir | ProgresReveScientifique => Troisieme +    } +  }  } diff --git a/src/main/scala/reading/models/GroupedTheme.scala b/src/main/scala/reading/models/GroupedTheme.scala index 61a5281..1a26bc1 100644 --- a/src/main/scala/reading/models/GroupedTheme.scala +++ b/src/main/scala/reading/models/GroupedTheme.scala @@ -39,4 +39,23 @@ object GroupedTheme extends Enum[GroupedTheme] {    case object Mort extends GroupedTheme    case object Difference extends GroupedTheme    case object Imaginaire extends GroupedTheme + +  def from(theme: Theme): GroupedTheme = { +    import Theme._ + +    theme match { +      case Art | Americain | Danse | Litterature | Musique | Mythologie | Poetique | Reecriture | Sport => Culture +      case Nature | Aventure | Initiation | Pirate | Voyage | Animal => Decouverte +      case Dragon | Magie => Imaginaire +      case Mutant | Beaute | Handicap | Homosexualite | Laideur | Metamorphose | Marginalite | Migration | Metissage | Pauvrete | Discrimination => Difference +      case Maltraitance | Viol | Combat | Conflit | Crime | Harcelement | Exclusion | Racisme | Antisemitisme | SegregationRaciale | Prison => Violence +      case Chevalerie | Cour | Dictature | Guerre | Nazisme | Revolution => Histoire +      case Theme.Humour => GroupedTheme.Humour +      case Enquete | Manipulation | Mensonge | Secret | Espionnage | Complot => Stratageme +      case Fantome | Folie | Malediction | Vampire | Monstrueux => Peur +      case Suicide | Deuil | Maladie | Immortalite | Vieillesse => Mort +      case Adolescence | Amour | Amitie | Apprentissage | College | Ecole | Emancipation | Sentiment | Sexualite | Enfants | Femme | Parents | Famille => Quotidien +      case Genetique | Internet | JeuVideo | Robot => Technologie +    } +  }  } diff --git a/src/main/scala/reading/models/Program.scala b/src/main/scala/reading/models/Program.scala index d33d23a..4c647f3 100644 --- a/src/main/scala/reading/models/Program.scala +++ b/src/main/scala/reading/models/Program.scala @@ -60,15 +60,4 @@ object Program extends Enum[Program] {    case object VisionsPoetiques extends Program    case object Agir extends Program    case object ProgresReveScientifique extends Program - -  def grade(program: Program): Grade = { -    import Grade._ - -    program match { -      case Monstre | RecitAventure | CreationPoetique | Resister => Sixieme -      case VoyageEtAventure | Autrui | UniversNouveaux | Heros | HommeEtNature => Cinquieme -      case DireAmour | Valeurs | Reel | Informer | Ville => Quatrieme -      case SeRaconter | TraversSociete | VisionsPoetiques | Agir | ProgresReveScientifique => Troisieme -    } -  }  } diff --git a/src/main/scala/reading/models/Theme.scala b/src/main/scala/reading/models/Theme.scala index 66870a4..2a76611 100644 --- a/src/main/scala/reading/models/Theme.scala +++ b/src/main/scala/reading/models/Theme.scala @@ -173,23 +173,4 @@ object Theme extends Enum[Theme] {    case object Vieillesse extends Theme    case object Viol extends Theme    case object Voyage extends Theme - -  def grouped(theme: Theme): GroupedTheme = { -    import GroupedTheme._ - -    theme match { -      case Art | Americain | Danse | Litterature | Musique | Mythologie | Poetique | Reecriture | Sport => Culture -      case Nature | Aventure | Initiation | Pirate | Voyage | Animal => Decouverte -      case Dragon | Magie => Imaginaire -      case Mutant | Beaute | Handicap | Homosexualite | Laideur | Metamorphose | Marginalite | Migration | Metissage | Pauvrete | Discrimination => Difference -      case Maltraitance | Viol | Combat | Conflit | Crime | Harcelement | Exclusion | Racisme | Antisemitisme | SegregationRaciale | Prison => Violence -      case Chevalerie | Cour | Dictature | Guerre | Nazisme | Revolution => Histoire -      case Theme.Humour => GroupedTheme.Humour -      case Enquete | Manipulation | Mensonge | Secret | Espionnage | Complot => Stratageme -      case Fantome | Folie | Malediction | Vampire | Monstrueux => Peur -      case Suicide | Deuil | Maladie | Immortalite | Vieillesse => Mort -      case Adolescence | Amour | Amitie | Apprentissage | College | Ecole | Emancipation | Sentiment | Sexualite | Enfants | Femme | Parents | Famille => Quotidien -      case Genetique | Internet | JeuVideo | Robot => Technologie -    } -  }  } | 
