Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
  • Development

    12 blog articles in this category

      Atomic CSS - what is it and is it useful?

      Atomic Design, or Functional Design as it is also referenced, is a bit of a weird beast. On one hand, it is a programmatic shorthand feature that allow you to use short code in development that then render CSS based on that, but it also a philosophy of sorts on how to organize CSS. A philosophy that very much resemble the inline-css era of old.
      The problem statement
      I think this statement says a lot on how ACSS and to some degree Atomic Design itself see the world of CSS. Anyone working with wild or free CSS development can certainly recognize some of these pain points. This is not uncommon in scenarios where many developers work in the same project or you have to work on top of a not so well-designed product.
      The question is, though, how many still work in that way and why?
      In a scenario where you have a defined design pattern, which should be the norm these days, this is rarely an issue. Regardless if you do atomic design or component design, your styles should be easy to maintain and define. Despite this I still see a lot of JS developers adopt inline styling and duplication of CSS in components. This is more common in libraries like React and Angular, I feel (personal experience, no real numbers to support that).
      So it is not surprising to see a rise in areas like Atomic Design or ACSS since that help bridge the gap in skill levels some JS developers have and the inherent despise we have to spend more time than we have to on things we don't really enjoy doing. Not to mention, there are some benefits that should not be overlooked.
      ACSS - Atomic Design as a preprocessor
      ACSS is a preprocessor that will transform inline HTML variables and create CSS from it. It has a fairly simple syntax and it works by looping though the HTML and scrape up the variables and then make a CSS file from what it finds.  In many ways, this is similar to inline CSS, but you would get a static CSS file instead of just printing it out in a style tag.
      If we borrow an example from the excellent CSS-Tricks site that I highly recommend you check out, then here is a snippet of code where we declare some classes and input a variable to each.
      <!-- Programmatic Atomic CSS Example --> <div class="Bgc(#0280ae) C(#fff) P(20px)">Lorem ipsum</div> The output from this would then generate the following CSS:
      .Bgc\(#0280ae\) { background-color: #0280ae; } .C\(#fff\) { color: #fff; } .P\(20px\) { padding: 20px; } This may seem quite useless compared to a well-defined CSS with variables declared that I could just input, but you could use both of course and use this to override in certain situations. Similar to how you would use inline styling.
      The biggest benefit of this however is that the CSS generated would be as small as you could make it. This is because the CSS would be based only on what can be found in the HTML. This can be useful for development in for example Angular and React, where I assume this is mostly used.
      In situations where you have classes reused over many components, like this website however, that would not really be as beneficial as having multiple CSS files is never a good thing and you would see duplication of classes. This is of course if you generate the CSS on a modular basis, but not if you render it globally on load somehow.
      Overall, I do not see any need for me working with sites like Invision Community and I feel it would not really work well in an atomic design situation with defined styles in both the UI and the HTML/CSS that is reused across components.
      I may be completely wrong about that, as I have not tried it and if so, sign off in the comments to tell me I am wrong ūüôā
       
      Atomic Design as a Philosophy
      The idea of Atomic CSS is simple and, to be honest, not very new. The idea is to create single attribute classes and then stuff the HTML with them. Kind of like you would do using Inline styling, but with a global definition of the styles. It would look something like this.
      /* naming utility classes */ .relative { position: relative; } .mt10 { margin-top: 10px; } .pb10 { padding-bottom: 10px; } I say that this is nothing new because this has been around since before inline styling even existed. One reason it is not widely used is because it makes the HTML cluttered and hard to read. It is also a bit annoying to work with compared to having well-defined CSS classes as you will have a ton of attribute shortcuts basically rather than a library of styles.
      If you are used to working with frameworks like bootstrap, then you are already familiar with the concept, since those frameworks also have libraries of smaller styles that you can mix and match. Atomic CSS is much smaller however and rather than having for example a flexbox class that have the standard values you use all over the site you have to build those up with a set of classes inline in the HTML.
      Is it a terrible idea?
      Atomic design as a concept can be traced back to October 2013 and an article written by Thierry Koblenz. It has since evolved and you will often see it in reference from the new breed of JS developers that work in frameworks like React and Angular. While it may be a bit unfair to say that these new JS developers do not have that same deep understanding of CSS as the "regular" front end developers, I do see this distinction growing.
      For the JS developers however, this is pretty useful as they can focus on function rather than form. You have probably seen the mess that sometimes happen when JS developers mix inline styling, component specific styling and global styling. It is as bad as having multiple novis CSS developers trying to use EM when they do not control the output containers...
      I hear some arguments that this makes it easier to avoid specificity issues, but I do not agree with that. If you control your code, then you are a pretty bad developer if you can't handle your classes properly. It all comes down to planning and setting structures after all, unless you are just playing around like I do on this site.
      I digress though and to answer the question if this is a terrible idea I think there are use cases for when this can be useful. Since that article in 2013 however, I think those use cases are less now, especially with the introduction of CSS variables. I do however think there is a very good case for using this same idea for defining variables and to keep your classes small as a philosophy.
      I do think there are certain scenarios where it is a good idea to use the concept of Atomic design, and that is for overrides. Things like fonts, colors and spacing can be useful to sometimes just make a small adjustment rather than building new components.
      Also, if you generate your content at runtime on the server, there are benefits to this since you can combine it with CSS-in-JS to dynamically generate very small CSS files. I guess this is why React and Angular developers are liking this way to organize your CSS classes. You can still do this working with regular CSS, but the files will be bigger since you are bound to have duplicate attributes in your classes.
      So it is not a terrible idea, but it comes with some issues.
      Allow me to explain...
      My Thoughts
      While there are benefits in some cases, there are some negatives as well. The biggest issue for me is that while we reduce the CSS, we instead increase the size of the HTML. The HTML also becomes much more cluttered and harder to read because unlike CSS where we can group classes, or even split them on multiple files, HTML will just be one big garbled mess.
      Consistency is also an issue because the idea of recreate clusters of classes to achieve the components is done manually every time, then you will see consistency go out the window. We see this all the time in wild or free CSS, where pretty much every component have its own class styles uniquely. This is especially problematic if you have JS developers that are not very good at using CSS and you try to set up a design library.
      At the end of the day however, front-end developers are flexible and resourceful people. All of these potential pitfalls can be managed and if Atomic CSS works for you and those that you collaborate with, then go for it. Just don't make the decision just in the development team as you need to work well with both Design and Test so they should have a say as well. Also, your way of working must work across teams, so don't start using Atomic CSS if you at any time will have other teams working in the code as well.
      You know all that of course, but it is something that you can never repeat too many times.
       
      Do you use Atomic Design today?
      If you do, what benefits and pitfalls have you uncovered?

      Building a Movies database - using plugin and some styling

      This week I took a look at a plugin I had and decided to style it a bit to fit the image I had in my mind. As it turned out this was very easy and the only thing that stopped me for a while was how to use custom fields. Once that was done it was smooth sailing and quite fun to customize. This is how I did it, so you can do it yourself if you want.
      The app that we will be working with is the Movies & TV Shows by Adriano Faria. This is an amazing app that use the TMDB database to fetch data for the movies and TV shows, making adding movies and TV shows fast and fun. Adriano has several similar apps and I also have the Books version that I probably look at later as well.
      In order to modify the app we need to go to the themes (customization -> Apperance -> Themes) and click the Edit HTML & CSS button. This takes us to the templates for our theme. There you will have two tabs, one for templates and one for CSS. In both of those tabs we will have a section called "Movies". This is where we find the app templates and the CSS that the app use.

       
      Inside the templates we will focus on a few templates to make the design that we want to make.
      View -> View: This is the template that control the layout for when you look at a movie or tv show. Index -> indexBlock: This is the template that control the latest and random items block on the start page. Index -> featuredMovie: This is the template that control the featured movies block on the start page. front -> browse -> rows: This is the template that control the listing inside a category. We will also work with the CSS that is located in the CSS tab:
      movies -> front -> movies.css  
      Changing the view
      The first thing I did was to change the View template. I had the idea that I wanted to have a big background image behind the content. This is a bit tricky since the content is trapped inside a div that have a fixed width. The second thing I wanted was to add more data and I especially wanted to add a custom rating icon for my rating since I will use this as my recommendation site of sorts.
      So first I needed to figure out how to add custom fields and decide what fields I wanted. I decided on the following fields:
      Official Trailer (ID:8) - The ID of the trailer, so I can add it to an embedded video player Certification (ID:6) - Just a text field to add the certification. TV Year (ID:4)  - Just a text field here as well. My Review (ID:2) - An editor field, so I can write my review. My Rating (ID:5) - Just a number field, so I can add values between 1 and 100. Backdrop (ID:1) - An upload field, so I can add a big image behind the content. In order to show these fields I need to use this little piece of code:
      {{$fields = $movie->customFields();}}{$fields['field_1']} I then just change the field_1 to the field I want to display. You can see what the id of your field is by hovering over the edit button in the custom fields view.
      I also want to be able to add some conditions, so I only see things if the field is filled in and in order to do that I use this:
      {{if $movie->customFields('field_1')}} With that we can now add our custom fields to the template and start looking at how to style them.
       
      The Backdrop
      The backdrop is shown by displaying the file that we uploaded, but it will be constrained within the content and we want to break it out from that. This is where we use a little hack in CSS and I added a class to the div surrounding the backdrop and then added this CSS to it:
      .full-width { width: 100vw; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; margin-top:-15px; min-height:800px; background-image:url(https:{media="528"}); background-color: #294459; background-position:top center; background-repeat:no-repeat; background-size:cover; } This now show the image in full width and I have this idea that I want to add a play button to the backdrop that show a trailer when I click it. There are several ways to do this, but I decided to use the built-in function in Invision Community called ips.ui.dialog. This nifty function allow me to open a modal and either populate it with a link or content inside the same page. As it does not work to refer directly to the url of the trailer, we use the embed code instead and place the content in a hidden div in the document.
      {{if $movie->customFields('field_1')}} <div class="full-width" style="background-image:url('https://jimiwikman.se/uploads/{{$fields = $movie->customFields();}}{$fields['field_1']}');"> <!-- Official Trailer --> {{if $fields['field_8']}} <a href='#dialogContent' data-ipsDialog data-ipsDialog-size="fullscreen" data-ipsDialog-content='#dialogContent'> <div class="JWSE_play"> <span class="JWSE_playBtn"><i class="far fa-play-circle"></i></span> <span class="JWSE_playTxt">Play Trailer</span> </div> </a> <div id="dialogContent" class="JWSE_video-container ipsHide"> <iframe src="https://www.youtube.com/embed/{$fields['field_8']}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> {{endif}} In the code I refer to the custom field I created for the official trailer that hold the movie ID. I also wrap the code in an if query, so it only shows if a value is entered. I use FontAwesome to show the play icon and I define the modal to be full view using the data-ipsDialog-size attribute. Currently, narrow, medium, wide and full screen are supported as values for this option.
      I then style this with the following CSS:
      /*** Youtube Popup ***/ .JWSE_video-container { overflow: hidden; position: relative; width:100%; } .JWSE_video-container::after { padding-top: 56.25%; display: block; content: ''; } .JWSE_video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .JWSE_play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align:center; height:170px; width:170px; background: rgb(41,68,89, 0.5); display:block; border-radius:100% } .JWSE_playBtn{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size:70px; opacity:0.9; color:white; display:block; } .JWSE_playTxt{ position: absolute; top: 80%; left: 50%; transform: translate(-50%, -50%); font-size:16px; color:white; display:block; }  
      My Rating
      Adding a rating icon to make the rating number look cool is next, and I wanted a circle with numbers inside. This can be done in any number of ways, but I decided on a fairly simple version of a SVG circle using css keyframes for animation.
      <!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} As you can see in the code I have two versions of the circle, one for when I have added a value and one for when I have not. I had to add a condition to the second version because as I have a number field it will always be 0, which means that it is never empty. This means that the first condition will always be true, even if I have not added anything to the field.
      To this I added the following CSS:
       
      /*** My Rating Circle ***/ .jwse_MyReviewText{ position:relative; } .jwse_ratingCircle { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:100px; } .jwse_ratingCircle-indexBlock { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:50px; } .jwse_ratingCircle-categoryList { float: left; position: absolute; top: -5px; left: 112px; z-index:999; width:50px; } .circular-chart{ display: block; margin: 10px auto; max-width: 80%; max-height: 100px; background-color: var(--color-baseblue); border-radius: 50%; } .circular-chart-indexBlock{ max-width:100%; max-height:50px; top:-6px; right: 3px; position:relative; } .circular-chart-Featured{ max-height:50px; top:-5px; right: 17px; position:relative; } .circular-chart_unrated{ background-color: var(--color-palegrey); } .circle-bg { fill: none; stroke: #eee; stroke-width: 3.8; opacity: 0.3; } .circle { fill: none; stroke-width: 3.8; stroke-linecap: round; animation: progress 1s ease-out forwards; } @keyframes progress { 0% { stroke-dasharray: 0 100; } } .circular-chart.orange .circle { stroke: var(--color-jwsecoral); } .percentage { fill: white; font-family: Montserrat; font-size: 13px; text-anchor: middle; } .percentageSmall{ fill: white; font-family: Montserrat; font-size: 5px; text-anchor: middle; } .unrated{ font-size:5px; } As you can see I have variants for the indexBlock and featuredBlock as well as adustment for the category list. This because the images are different for those areas and we need to adjust a bit.
       
      Let's put it all together
      With this I now have all the custom fields shown on the page, a backdrop that cover the full width of the page and I have a visual representation of the rating. All that is left is to put things together in the templates and add styles to it. We also need to adjust a bit for mobile view.
      With permission from Adriano, I will show you the code I use, and you can copy and paste it into your own movies theme files to get the layout I use here on this site. I have not cleaned these up, so there are some areas that need cleaning, but it works.
       
      View Rows indexBlock featuredMovie CSS <div class="ipsRichEmbed" style="max-width: 500px; border: 1px solid rgba(0,0,0,0.1); "> <div class="ipsRichEmbed_masthead ipsRichEmbed_mastheadBg ipsType_center"> <a href="https://www.vowel.com/" rel="external nofollow" style="background-image: url( 'https://s3.amazonaws.com/com.vowel.resources/social/vowel-share-thumb.jpg' ); background-position: center; background-repeat: no-repeat; background-size: cover; height: 120px; display: block;"><img alt="vowel-share-thumb.jpg" class="ipsHide" data-loaded="true" data-src="https://s3.amazonaws.com/com.vowel.resources/social/vowel-share-thumb.jpg" src="https://jimiwikman.se/applications/core/interface/js/spacer.png" style="height: auto;"></a> </div> <div style="padding: 10px;"> <h3 class="ipsRichEmbed_itemTitle ipsTruncate ipsTruncate_line ipsType_blendLinks"> <a href="https://www.vowel.com/" rel="external nofollow" style="text-decoration: none; margin-bottom: 5px;" title="Home - Vowel">Home - Vowel</a> </h3> <div class="ipsType_light"> WWW.VOWEL.COM </div> <hr class="ipsHr"> <div class="ipsSpacer_top ipsSpacer_half" data-ipstruncate="" data-ipstruncate-size="3 lines" data-ipstruncate-type="remove" style="overflow-wrap: break-word;"> <span>Vowel is a tool that captures your team&rsquo;s meetings. Use it to annotate meetings in real-time, tag action items, call back ideas verbatim&mdash;and a whole lot more.</span> </div> </div> </div> <div class="ipsTabs ipsTabs_contained ipsTabs_withIcons ipsTabs_large ipsTabs_stretch ipsClearfix" data-ipstabbar="" data-ipstabbar-contentarea="#" data-ipstabbar-defaulttab="1" data-ipstabbar-updateurl="false" id="elTabBar"> <ul role="tablist"> <li> <a aria-selected="true" class="ipsType_center ipsTabs_item ipsTabs_activeItem" href="" id="1" rel="" role="tab"> View </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="2" rel="" role="tab"> Rows </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="3" rel="" role="tab"> indexBlock </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="4" rel="" role="tab"> featuredMovie </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="5" rel="" role="tab"> CSS </a> </li> </ul> </div> <section class="ipsTabs_panels ipsTabs_contained"> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_1_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> {{if $club = $movie->container()->club()}} {{if settings.clubs and settings.clubs_header == 'full'}} {template="header" app="core" group="clubs" params="$club, $movie->container()"} {{endif}} <div id='elClubContainer'> {{endif}} {{$fields = $movie->customFields();}} {{if $fields['field_1']}} <div class="full-width" style="background-image:url('https://jimiwikman.se/uploads/{$fields['field_1']|raw}');"> <!-- Official Trailer --> {{if $fields['field_8']}} <a href='#dialogContent' data-ipsDialog data-ipsDialog-size="fullscreen" data-ipsDialog-content='#dialogContent'> <div class="JWSE_play"> <span class="JWSE_playBtn"><i class="far fa-play-circle"></i></span> <span class="JWSE_playTxt">Play Trailer</span> </div> </a> <div id="dialogContent" class="JWSE_video-container ipsHide"> <iframe src="https://www.youtube.com/embed/{$fields['field_8']}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> {{endif}} </div> {{else}} <div class="full-width" style="background-image:url('{media="528"}')"></div> {{endif}} <div class=""> <div class="ipsPageHeader ipsClearfix ipsSpacer_bottom"> {template="contentItemMessages" group="global" app="core" params="$movie->getMessages(), $movie"} <div class='ipsBox ipsResponsive_pull'> <div class='ipsColumns ipsColumns_collapsePhone'> <aside class='ipsColumn ipsColumn_veryWide ipsPadding_right:none'> <div class='ipsPad lg:ipsPos_sticky'> <div> {{if $movie->cover_thumb}} <div class='ipsPos_center'> <div class="sosMovieCover"> <!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, '', $movie->title, 'large'"} </div> <div class="jwse_MoviePhoto_Footer"> {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; <span class='ipsType_normal ipsType_light'>({lang="num_reviews" pluralize="$movie->reviews"})</span> </div> </div> {{else}} <center><img src='{resource="movie.PNG" app="movies" location="front"}'></center> {{endif}} <hr class='ipsHr'> {{if $movie->imdb_id OR $movie->homepage OR $movie->wikipedia OR $movie->instagram OR $movie->facebook OR $movie->twitter OR $movie->justwatch}} <ul class='ipsList_inline ipsSpacer_both ipsType_center'> {{if $movie->homepage}} <li><a title="{lang="movies_homepage_visit"}" data-ipsTooltip target="_blank" href="{$movie->homepage}"> <i class="fa fa-link fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->imdb_id}} <li><a title="{lang="movies_imdb_page_visit"}" data-ipsTooltip target="_blank" href="https://www.imdb.com/title/{$movie->imdb_id}/"> <i class="fa fa-imdb fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->facebook}} <li><a title="{lang="movies_facebook_page"}" data-ipsTooltip target="_blank" href="{$movie->facebook}"> <i class="fa fa-facebook fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->instagram}} <li><a title="{lang="movies_instagram_page"}" data-ipsTooltip target="_blank" href="{$movie->instagram}"> <i class="fa fa-instagram fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->justwatch}} <li><a title="{lang="movies_justwatch_page"}" data-ipsTooltip target="_blank" href="{$movie->justwatch}"> <i class="fa fa-play fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->twitter}} <li><a title="{lang="movies_twitter_page"}" data-ipsTooltip target="_blank" href="{$movie->twitter}"> <i class="fa fa-twitter fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->wikipedia}} <li><a title="{lang="movies_wikipedia_page"}" data-ipsTooltip target="_blank" href="{$movie->wikipedia}"> <i class="fa fa-wikipedia-w fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->canEdit()}} <li> <a href="{$movie->url()->csrf()->setQueryString( array( 'do' => 'editUrls' ) )}" data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="movie_edit_urls"}"> <i data-ipsTooltip title="{lang='movie_edit_urls'}" class="fa fa-pencil fa-fw" aria-hidden="true"></i> </a> </li> {{endif}} </ul> <hr class='ipsHr'> {{endif}} <ul class="ipsDataList ipsDataList_reducedSpacing ipsSpacer_top"> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_type"}</strong></span> <span class="ipsDataItem_generic">{lang="movie_type_{$movie->type}"}</span> </li> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_status"}</strong></span> <span class="ipsDataItem_generic">{$movie->status}</span> </li> {{if $movie->type=='tv'}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_seasons"}</strong></span> <span class="ipsDataItem_generic">{$movie->seasons}</span> </li> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_episodes"}</strong></span> <span class="ipsDataItem_generic">{$movie->episodes}</span> </li> {{endif}} {{if $movie->type=='movie'}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_original_language"}</strong></span> {{$language = \IPS\movies\Movie::getLanguageName( $movie->original_language );}} <span class="ipsDataItem_generic">{$language}</span> </li> {{if $movie->budget}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_budget"}</strong></span> <span class="ipsDataItem_generic">{expression="\IPS\Member::loggedIn()->language()->formatNumber( $movie->budget, 2 )"}</span> </li> {{endif}} {{if $movie->revenue}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_revenue"}</strong></span> <span class="ipsDataItem_generic">{expression="\IPS\Member::loggedIn()->language()->formatNumber( $movie->revenue, 2 )"}</span> </li> {{endif}} {{endif}} </ul> {{if $movie->topic()}} <hr class='ipsHr'> <a href='{$movie->topic()->url()}' title='{lang="movie_discussion_topic"}' class='ipsButton ipsButton_primary ipsButton_fullWidth'>{lang="movie_discussion_topic"}</a> {{endif}} <div class='ipsResponsive_showPhone ipsResponsive_block ipsSpacer_top'> {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> </div> </div> </aside> <article class='ipsColumn ipsColumn_fluid ipsPadding_left:none'> <div class='ipsPad'> <section class='ipsType_normal whiteBack'> <div class='ipsPageHeader ipsResponsive_pull ipsBox ipsPadding ipsSpacer_bottom'> <div class='ipsFlex ipsFlex-ai:center ipsFlex-fw:wrap ipsGap:4'> <div class='ipsFlex-flex:11'> <h1 class='ipsType_pageTitle ipsContained_container'> {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="movies_locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{elseif $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === -2}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->deletedBlurb()}'><i class='fa fa-trash'></i></span></span> {{endif}} {{if $movie->canToggleItemModeration() and $movie->itemModerationEnabled()}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="topic_moderation_enabled"}'><i class='fa fa-user-times'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{if $movie->canEdit()}} <span class='ipsType_break ipsContained' data-controller="core.front.core.moderation"> {{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<span data-role="editableTitle" title='{lang="click_hold_edit"}'>{$movie->title}</span> </span> {{else}} <span class='ipsType_break ipsContained'>{{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}{$movie->title}</span> {{endif}} {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="jwse_movieYear">( {$date->format('Y')} )</span> {{endif}} {{if $fields['field_4']}} <span class="jwse_movieYear">( {{$fields = $movie->customFields();}}{$fields['field_4']} )</span> {{endif}} {{if $movie->prefix() OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}} <span {{if !$movie->prefix()}}class='ipsHide'{{endif}} {{if ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}}data-editablePrefix{{endif}}> {template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"} </span> {{endif}} </h1> <div class="jswe_movieMeta"> {{if $fields['field_6']}} <span class="ipsDataItem_generic certification">{{$fields = $movie->customFields();}}{$fields['field_6']|raw}</span> {{endif}} <!-- Movie Release Date --> {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="ipsDataItem_generic">{datetime="$date" dateonly="true"}</span> {{endif}} <!-- Movie Genres --> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <span class="ipsDataItem_generic">{$genres}</span> <!-- Movie Runtime --> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <span class="ipsDataItem_generic">{$runtime}</span> {{endif}} </div> {{if $movie->subtitle}} <span class="jwse_movieSubtitle">{$movie->subtitle}</span> {{endif}} <span class="jwse_movieDescription">{lang="meta_description"}</span> <p> {$movie->description|raw} </p> {{if \count( $movie->tags() ) OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) )}} {template="tags" group="global" app="core" params="$movie->tags(), FALSE, FALSE, ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) ) ? $movie->url() : NULL"} {{endif}} </div> </div> <hr class='ipsHr'> <div class='ipsPageHeader__meta ipsFlex ipsFlex-jc:between ipsFlex-ai:center ipsFlex-fw:wrap ipsGap:3'> <div class='ipsFlex-flex:11'></div> <div class='ipsFlex-flex:01 ipsResponsive_hidePhone'> <div class='ipsFlex ipsFlex-ai:center ipsFlex-jc:center ipsGap:3 ipsGap_row:0'> {{if \count( $movie->shareLinks() )}} {template="shareButton" app="core" group="sharelinks" params="$movie"} {{endif}} {template="promote" app="core" group="global" params="$movie"} {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> </div> </div> </div> {{if $movie->hidden() === 1 and $movie->canUnhide()}} <div class="ipsMessage ipsMessage_warning ipsSpacer_both"> <p class="ipsType_reset">{lang="movie_pending_approval"}</p> <br> <ul class='ipsList_inline'> <li><a href="{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) )}" class="ipsButton ipsButton_positive ipsButton_verySmall" title='{lang="approve_title_link"}'><i class="fa fa-check"></i> {lang="approve"}</a></li> {{if $movie->canDelete()}} <li><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete' ) )}' data-confirm title='{lang="movies_delete_title"}' class='ipsButton ipsButton_negative ipsButton_verySmall'><i class='fa fa-times'></i> {lang="delete"}</a></li> {{endif}} </ul> </div> {{endif}} <div class='ipsTabs ipsClearfix' id='elTabBar' data-ipsTabBar data-ipsTabBar-contentArea='#elTabContent' data-ipsTabBar-updateURL='false'> <a href='#elTabBar' data-action='expandTabs'><i class='fa fa-caret-down'></i></a> <ul role='tablist'> <li role='presentation'> <a href='{$movie->url()}' role='tab' id='elTabMovie' class='ipsTabs_item' aria-selected="true"> My Thoughts </a> </li> {{if \IPS\Settings::i()->movies_cast}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'cast' ) )}' id='elTabCast' role='tab' class='ipsTabs_item'> {lang="movies_tab_cast"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_crew}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'crew' ) )}' id='elTabCrew' role='tab' class='ipsTabs_item'> {lang="movies_tab_crew"} </a> </li> {{endif}} {{if $movie->type == 'tv'}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'seasons' ) )}' id='elTabEpisodes' role='tab' class='ipsTabs_item'> {lang="movies_tab_seasons"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_videos}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'videos' ) )}' id='elTabVideos' role='tab' class='ipsTabs_item'> {lang="movies_tab_videos"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_posters}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'posters' ) )}' id='elTabPosters' role='tab' class='ipsTabs_item'> {lang="movies_tab_posters"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_images}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'images' ) )}' id='elTabImages' role='tab' class='ipsTabs_item'> {lang="movies_tab_images"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_similar}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'similar' ) )}' id='elTabSimilar' role='tab' class='ipsTabs_item'> {{if $movie->type == 'movie'}} {lang="movies_tab_similar"} {{else}} {lang="movies_tab_similar_tv"} {{endif}} </a> </li> {{endif}} </ul> </div> <section id='elTabContent'> <div class="ipsBox ipsPad"> <div itemprop='text' data-controller='core.front.core.lightboxedImages'> <div class="jwse_MyReviewFace"></div> <div class="jwse_MyReviewText"> <span class="jwse_MyReviewTextRating"> <!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{else}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} </span> {{if $fields['field_2']}} {{$fields = $movie->customFields();}}{$fields['field_2']|raw} {{else}} <p> <i>I have not written a review for this {{if $movie->type == 'movie'}}Movie{{else}}TV Show{{endif}} yet.<br /> Write a comment and remind me to write one!</i> </p> {{endif}} </div> <br style="clear:both;" /> </div> {{if $movie->container()->auto_content AND \IPS\Member::loggedIn()->language()->checkKeyExists( "movies_category_{$movie->container()->id}_append_content" )}} {{$lang = \IPS\Member::loggedIn()->language()->addToStack( "movies_category_{$movie->container()->id}_append_content" );}} {{\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $lang );}} {{$content = str_replace( "%MOVIENAME%", $movie->title, $lang );}} {{\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );}} {$content|raw} {{endif}} </div> </section> </section> </div> </article> </div> {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') ) or $movie->canReportOrRevoke() or ( \IPS\IPS::classUsesTrait( $movie, 'IPS\Content\Reactable' ) and settings.reputation_enabled )}} <div class='ipsItemControls'> {{if \IPS\IPS::classUsesTrait( $movie, 'IPS\Content\Reactable' ) and settings.reputation_enabled}} {template="reputation" app="core" group="global" params="$movie"} {{endif}} {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') ) or $movie->canReportOrRevoke()}} <ul class='ipsComment_controls ipsClearfix ipsItemControls_left'> {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') )}} <li> <a href='#elMovieActions_menu' id='elMovieActions' class='ipsButton ipsButton_light ipsButton_verySmall ipsButton_fullWidth' data-ipsMenu>{lang="{$movie->type}_actions"} <i class='fa fa-caret-down'></i></a> <ul id='elMovieActions_menu' class='ipsMenu ipsMenu_auto ipsHide'> {{if $movie->canReportOrRevoke() === TRUE}} <li class='ipsMenu_item'> <a href='{$movie->url('report')}' data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="report_movie"}" data-ipsDialog-remoteSubmit data-ipsDialog-flashMessage="{lang="report_submit_success"}" title="{lang="report_movie"}" >{lang="report"}</a> </li> <li class='ipsMenu_sep'><hr></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') AND $movie->hidden() == -2}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restore' ) )}' data-confirm data-confirmSubMessage='{lang="restore_as_visible_desc"}'>{lang="restore_as_visible"}</a></li> <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restoreAsHidden' ) )}' data-confirm data-confirmSubMessage='{lang="restore_as_hidden_desc"}'>{lang="restore_as_hidden"}</a></li> <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete', 'immediate' => 1 ) )}' data-confirm data-confirmSubMessage='{lang="delete_immediately_desc"}'>{lang="delete_immediately"}</a></li> {{else}} {{if $movie->canEdit()}} <li class='ipsMenu_item'><a href='{$movie->url()->setQueryString( array( 'do' => 'edit' ) )}' title='{lang="edit"}'>{lang="edit"}</a></li> {{endif}} {{if $movie->canFeature()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'feature' ) )}'>{lang="feature"}</a></li> {{endif}} {{if $movie->canUnfeature()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unfeature' ) )}'>{lang="unfeature"}</a></li> {{endif}} {{if $movie->canPin()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) )}'>{lang="pin"}</a></li> {{endif}} {{if $movie->canUnpin()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unpin' ) )}'>{lang="unpin"}</a></li> {{endif}} {{if $movie->canHide()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'hide' ) )}' data-ipsDialog data-ipsDialog-title="{lang="hide"}">{lang="hide"}</a></li> {{endif}} {{if $movie->canUnhide()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) )}'>{{if $movie->hidden() === 1}}{lang="approve"}{{else}}{lang="unhide"}{{endif}}</a></li> {{endif}} {{if $movie->canLock()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'lock' ) )}'>{lang="lock"}</a></li> {{endif}} {{if $movie->canUnlock()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unlock' ) )}'>{lang="unlock"}</a></li> {{endif}} {{if $movie->canMove()}} <li class='ipsMenu_item'><a href='{$movie->url()->setQueryString( array( 'do' => 'move' ) )}' data-ipsDialog data-ipsDialog-size='narrow' data-ipsDialog-title="{lang="move"}">{lang="move"}</a></li> {{endif}} {{if $movie->canDelete()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete' ) )}' data-confirm>{lang="delete"}</a></li> {{endif}} {{if $movie->canOnMessage( 'add' )}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'messageForm' ) )}' data-ipsDialog data-ipsDialog-title='{lang="add_message"}'>{lang='add_message'}</a></li> {{endif}} {{if $movie->canToggleItemModeration()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'toggleItemModeration' ) )}' data-confirm data-confirmMessage='{{if $movie->itemModerationEnabled()}}{lang="disable_movie_moderation_confirm"}{{else}}{lang="enable_movie_moderation_confirm"}{{endif}}'>{{if $movie->itemModerationEnabled()}}{lang="disable_topic_moderation"}{{else}}{lang="enable_topic_moderation"}{{endif}}</a></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_reimport_data')}} <li class='ipsMenu_sep'><hr></li> <li class="ipsMenu_item"><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'reimport' ) )}' data-confirm>{lang="movies_reimport_{$movie->type}_data"}</a></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_view_moderation_log')}} <li class='ipsMenu_sep'><hr></li> <li class="ipsMenu_item"><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'modLog' ) )}' data-ipsDialog data-ipsDialog-title='{lang="moderation_history"}'>{lang="moderation_history"}</a></li> {{endif}} {{endif}} </ul> </li> {{elseif $movie->canReportOrRevoke() === TRUE}} <li> <a href='{$movie->url('report')}' data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="report_file"}" data-ipsDialog-remoteSubmit data-ipsDialog-flashMessage="{lang="report_submit_success"}" title="{lang="report_file"}" class='ipsButton ipsButton_link ipsButton_verySmall ipsButton_fullWidth'>{lang="report"}</a> </li> {{endif}} </ul> {{endif}} </div> {{endif}} </div> <div class='ipsBox ipsPadding ipsResponsive_pull ipsResponsive_showPhone ipsMargin_top'> <div class='ipsGap_row:3'> <div> {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> {{if \count( $movie->shareLinks() )}} <div> {template="shareButton" app="core" group="sharelinks" params="$movie, 'verySmall', 'light'"} </div> {{endif}} <div> {template="promote" app="core" group="global" params="$movie"} </div> </div> </div> {{if $prev || $next}} <div class='ipsGrid ipsGrid_collapsePhone ipsPager ipsSpacer_top'> {{if $prev !== NULL}} <div class="ipsGrid_span6 ipsType_left ipsPager_prev"> <a href="{$prev->url()}" title="{lang="prev_movie"}" rel="prev"> <span class="ipsPager_type">{lang="prev_movie"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{wordbreak="$prev->mapped('title')"}</span> </a> </div> {{else}} <div class="ipsGrid_span6 ipsType_left ipsPager_prev"> <a href="{$movie->container()->url()}" title="{lang="go_to_category" sprintf="$movie->container()->_title"}" rel="up"> <span class="ipsPager_type">{lang="movie_back_to_category"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{lang="$movie->container()->_title" wordbreak="true"}</span> </a> </div> {{endif}} {{if $next !== NULL}} <div class="ipsGrid_span6 ipsType_right ipsPager_next"> <a href="{$next->url()}" title="{lang="next_movie"}" rel="next"> <span class="ipsPager_type">{lang="next_movie"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{wordbreak="$next->mapped('title')"}</span> </a> </div> {{else}} <div class="ipsGrid_span6 ipsType_right ipsPager_next"> <a href="{$movie->container()->url()}" title="{lang="go_to_category" sprintf="$movie->container()->_title"}" rel="up"> <span class="ipsPager_type">{lang="movie_back_to_category"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{lang="$movie->container()->_title" wordbreak="true"}</span> </a> </div> {{endif}} </div> <hr class='ipsHr'> {{endif}} {{if $commentsAndReviews}} <a id="replies"></a> <h2 class='ipsHide'>{lang="user_feedback"}</h2> <div class='ipsMargin_top'>{$commentsAndReviews|raw}</div> {{endif}} </div> </div> {{if $movie->container()->club()}} </div> {{endif}} <div class="ipsClear ipsClearfix"></div> </pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_2_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_3_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_4_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message </span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_5_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> </section>  
        {{if \count( $movies )}} {{$rowCount=0;}} {{foreach $movies as $movie}} {{$rowCount++;}} <li class='ipsDataItem {{if $movie->unread()}}ipsDataItem_unread{{endif}} {{if method_exists( $movie, 'tableClass' ) && $movie->tableClass()}}ipsDataItem_{$movie->tableClass()}{{endif}} {{if $movie->hidden()}}ipsModerated{{endif}}' data-ipsLazyLoad> <div class='ipsDataItem_generic ipsDataItem_size3 ipsPos_top'> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']}} <div class="jwse_ratingCircle jwse_ratingCircle-categoryList"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-categoryList orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle jwse_ratingCircle-categoryList"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-categoryList"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {{if $movie->cover_thumb}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'medium'"} {{else}} <a class="sosMovieCover_medium" title='{lang="view_this" sprintf="$movie->title"}' href="{$movie->url()}"> <img src='{resource="movie.PNG" app="movies" location="front"}'> </a> {{endif}} </div> <div class='ipsDataItem_main'> <h3 class='ipsDataItem_title ipsType_center ipsType_sectionHead ipsContained_container'> {{if $movie->unread()}} <span><span class='ipsItemStatus ipsItemStatus_small' data-ipsTooltip title='{{if $movie->unread() === -1}}{lang="new"}{{else}}{lang="updated"}{{endif}}'><i class="fa fa-circle"></i></span>&nbsp;</span> {{endif}} {{if $movie->mapped('locked') || $movie->mapped('pinned') || $movie->affiliate || $movie->mapped('featured') || $movie->hidden() === -1 || $movie->hidden() === 1}} {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_small ipsBadge_icon ipsBadge_negative" data-ipsTooltip title='{lang="locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{endif}} {{if $movie->prefix()}} <span>{template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"}</span> {{endif}} <span class='ipsType_break ipsContained'> <a href='{$movie->url()}' class='' title='{{if $movie->mapped('title')}}{$movie->mapped('title')}{{else}}{lang="content_deleted"}{{endif}} {{if $movie->canEdit()}}{lang="click_hold_edit"}{{endif}}' {{if $movie->canEdit()}} data-role="editableTitle"{{endif}} {{if $movie->tableHoverUrl and $movie->canView()}} data-ipsHover data-ipsHover-target='{$movie->url()->setQueryString('preview', 1)}' data-ipsHover-timeout='1.5'{{endif}}> <span> {{if $movie->mapped('title') or $movie->mapped('title') == 0}}{$movie->mapped('title')}{{else}}<em class="ipsType_light">{lang="content_deleted"}</em>{{endif}} </span> </a> </span> </h3> <p class='ipsType_light'>{lang="byline_nodate" htmlsprintf="$movie->author()->link()"}</p> <div class='ipsType_richText ipsType_normal ipsSpacer_both' data-ipsTruncate data-ipsTruncate-type="remove" data-ipsTruncate-size="2 lines"> {$movie->truncated(TRUE)|raw} </div> <p class='ipsType_reset ipsType_light ipsType_blendLinks'> {{if \IPS\Request::i()->app != 'movies'}} {lang="in"} <a href="{$movie->container()->url()}">{$movie->container()->_title}</a> {{endif}} </p> {{if \count( $movie->tags() )}} <div class=" ipsSpacer_top"> {template="tags" group="global" app="core" params="$movie->tags()"} </div> {{endif}} <p class='ipsType_reset'> <ul class="ipsList_inline"> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <li class='ipsType_light ipsType_blendLinks'>{lang="movie_running_{$movie->type}"}: {$runtime}</li> {{endif}} {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <li class='ipsType_light ipsType_blendLinks'>{$genres}</li> </ul> </p> </div> <div class='ipsDataItem_generic ipsDataItem_size8'> {{if $movie->container()->allowcomments}} <p class='ipsType_normal {{if !$movie->comments}}ipsType_light{{endif}}'> {{if $movie->comments}} <a href='{$movie->url()->setQueryString( 'tab', 'comments' )->setFragment('replies')}'> {{endif}} <i class='fa fa-comment'></i> {lang="num_comments" pluralize="$movie->comments"} {{if $movie->comments}} </a> {{endif}} </p> {{endif}} {{if $movie->container()->allowreviews}} {{if $movie->reviews}} <a href='{$movie->url()->setQueryString( 'tab', 'reviews' )->setFragment('reviews')}'> {{endif}} {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; <span class='ipsType_normal ipsType_light'>({lang="num_reviews" pluralize="$movie->reviews"})</span> {{if $movie->reviews}} </a> {{endif}} {{endif}} <p class='ipsType_medium'><strong>{{if $movie->updated == $movie->submitted}}{lang="submitted"} {datetime="$movie->submitted"}{{else}}{lang="updated"} {datetime="$movie->updated"}{{endif}}</strong></p> </div> {{if method_exists( $table, 'canModerate' ) AND $table->canModerate()}} <div class='ipsDataItem_modCheck'> <span class='ipsCustomInput'> <input type='checkbox' data-role='moderation' name="moderate[{$movie->id}]" data-actions="{expression="implode( ' ', $table->multimodActions( $movie ) )"}" data-state='{{if $movie->tableStates()}}{$movie->tableStates()}{{endif}}'> <span></span> </span> </div> {{endif}} </li> {{endforeach}} {{endif}}  
      <li class='ipsAreaBackground_reset ipsType_blendLinks ipsClearfix sosMovieIndexBlock sosMovieIndexBlockItem ipsPad_half ipsCarousel_item' data-ipsLazyLoad> <div class="indexBlock"> <div class='ipsPos_center'> <div class="sosMovieCover"> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} <a href='{$movie->url()}' title='{lang="view_this_movie" sprintf="$movie->title"}'> <div class=""> {{if $movie->cover_thumb}} {template="bgCover" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'big'"} {{else}} <a title='{lang="view_this" sprintf="$movie->title"}' href="{$movie->url()}"> <img class="bgMovieCover" src='{resource="movie.PNG" app="movies" location="front"}'> </a> {{endif}} </div> </a> <div class="jwse_MoviePhoto_Footer jwse_MoviePhoto_Footer-indexBlock"> {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; </div> </div> </div> </div> </li>  
      <li class='ipsCarousel_item ipsAreaBackground_reset ipsPad' data-ipsLazyLoad> <div class='ipsColumns ipsColumns_collapsePhone'> <div class='ipsColumn ipsColumn_medium ipsType_center'> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']|raw}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock jwse_ratingCircle-Featured"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock circular-chart-Featured orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{else}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock jwse_ratingCircle-Featured"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock circular-chart-Featured"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'large'"} </div> <div class='ipsColumn ipsColumn_fluid'> <h1 class='ipsType_pageTitle ipsContained_container'> {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="movies_locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{elseif $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === -2}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->deletedBlurb()}'><i class='fa fa-trash'></i></span></span> {{endif}} {{if $movie->canToggleItemModeration() and $movie->itemModerationEnabled()}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="topic_moderation_enabled"}'><i class='fa fa-user-times'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{if $movie->canEdit()}} <span class='ipsType_break ipsContained' data-controller="core.front.core.moderation"> {{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<span data-role="editableTitle" title='{lang="click_hold_edit"}'><a href='{$movie->url()}'>{$movie->title}</a></span> </span> {{else}} <span class='ipsType_break ipsContained'>{{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<a href='{$movie->url()}'>{$movie->title}</a></span> {{endif}} {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="jwse_movieYear">( {$date->format('Y')} )</span> {{endif}} {{if $fields['field_4']}} <span class="jwse_movieYear">( {{$fields = $movie->customFields();}}{$fields['field_4']} )</span> {{endif}} {{if $movie->prefix() OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}} <span {{if !$movie->prefix()}}class='ipsHide'{{endif}} {{if ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}}data-editablePrefix{{endif}}> {template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"} </span> {{endif}} </h1> <div class="jswe_movieMeta"> {{$fields = $movie->customFields();}} {{if $fields['field_6']}} <span class="ipsDataItem_generic certification">{{$fields = $movie->customFields();}}{$fields['field_6']|raw}</span> {{endif}} <!-- Movie Release Date --> {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="ipsDataItem_generic">{datetime="$date" dateonly="true"}</span> {{endif}} <!-- Movie Genres --> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <span class="ipsDataItem_generic">{$genres}</span> <!-- Movie Runtime --> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <span class="ipsDataItem_generic">{$runtime}</span> {{endif}} </div> <div class='ipsType_richText ipsType_normal ipsSpacer_both' data-ipsTruncate data-ipsTruncate-type="remove" data-ipsTruncate-size="2 lines"> {$movie->truncated(TRUE)|raw} </div> <p class='ipsType_reset'> <ul class="ipsList_inline"> {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <li class='ipsType_light ipsType_blendLinks'>{lang="movie_running_{$movie->type}"}: {$runtime} </li> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <li class='ipsType_light ipsType_blendLinks'>{$genres} </li> </ul> </p> <ul class='ipsToolList ipsToolList_horizontal ipsSpacer_top'> <li class='ipsPos_left'> <a href='{$movie->url()}' class='ipsButton ipsButton_light ipsButton_fullWidth ipsButton_small' title='{lang="view_this_movie" sprintf="$movie->title"}'>{lang="more_information"} <i class="far fa-play-circle"></i></a> </li> </ul> </div> </div> </li>  
      /*** Custom Styles ***/ .jwse_note{ color: #294459 } .ipsBadge_positive{ --badge--background: var(--color-palegrey); --badge--color: var(--color-jwsecoral); } /*** Youtube Popup ***/ .JWSE_video-container { overflow: hidden; position: relative; width:100%; } .JWSE_video-container::after { padding-top: 56.25%; display: block; content: ''; } .JWSE_video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .JWSE_play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align:center; height:170px; width:170px; background: rgb(41,68,89, 0.5); display:block; border-radius:100% } .JWSE_playBtn{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size:70px; opacity:0.9; color:white; display:block; } .JWSE_playTxt{ position: absolute; top: 80%; left: 50%; transform: translate(-50%, -50%); font-size:16px; color:white; display:block; } /*** My Rating Circle ***/ .jwse_MyReviewText{ position:relative; } .jwse_ratingCircle { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:100px; } .jwse_ratingCircle-indexBlock { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:50px; } .jwse_ratingCircle-categoryList { float: left; position: absolute; top: -5px; left: 112px; z-index:999; width:50px; } .circular-chart{ display: block; margin: 10px auto; max-width: 80%; max-height: 100px; background-color: var(--color-baseblue); border-radius: 50%; } .circular-chart-indexBlock{ max-width:100%; max-height:50px; top:-6px; right: 3px; position:relative; } .circular-chart-Featured{ max-height:50px; top:-5px; right: 17px; position:relative; } .circular-chart_unrated{ background-color: var(--color-palegrey); } .circle-bg { fill: none; stroke: #eee; stroke-width: 3.8; opacity: 0.3; } .circle { fill: none; stroke-width: 3.8; stroke-linecap: round; animation: progress 1s ease-out forwards; } @keyframes progress { 0% { stroke-dasharray: 0 100; } } .circular-chart.orange .circle { stroke: var(--color-jwsecoral); } .percentage { fill: white; font-family: Montserrat; font-size: 13px; text-anchor: middle; } .percentageSmall{ fill: white; font-family: Montserrat; font-size: 5px; text-anchor: middle; } .unrated{ font-size:5px; } /*** View CSS ***/ .full-width { width: 100vw; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; margin-top:-15px; min-height:800px; background-image:url(https:{media="528"}); background-color: #294459; background-position:top center; background-repeat:no-repeat; background-size:cover; } .ipsPageHeader.ipsClearfix.ipsSpacer_bottom { margin-top: -200px; } .ipsColumn_veryWide { width: 300px; } .ipsBox.ipsResponsive_pull { background-color: rgba(255, 255, 255, 0.6); } .whiteBack { font-size: 18px; background-color: white; border-radius:7px; } .ipsCarousel_inner { height: 230px !important; } .sosMovieCover_large img { margin: 0px auto; width: 100%; width: -moz-available; width: -webkit-fill-available; width: stretch; max-width: 300px !important; max-height: auto; border-top-right-radius: 7px; border-top-left-radius: 7px; } .jwse_MoviePhoto_Footer{ background-color: var(--color-baseblue); text-align:center; padding:15px 0; border-bottom-right-radius:7px; border-bottom-left-radius:7px; color:white !important; } .jwse_MoviePhoto_Footer-indexBlock{ position:absolute; display:block; width:100%; bottom:0; z-index:999; padding: 3px 0; margin-bottom:-28px; } .jwse_MoviePhoto_Footer-indexBlock .ipsRating.ipsRating_large{ font-size:10px !important; } .ipsRating .ipsRating_mine .ipsRating_on .fa-star { color: var(--color-jwsecoral) !important; } .jwse_movieYear{ font-size:16px; } .ipsDataItem_generic{ display: table-cell; padding: 5px 15px 15px 0; vertical-align: center; } .ipsDataItem_generic.certification { border: 1px solid #666; padding: 0 5px; display: block; float: left; margin: 5px; } .jwse_movieSubtitle{ font-weight:600; font-style: italic; display:block; } .jwse_MyReviewFace{ background-image:url(https:{media="530"}); background-size:cover; width:100px; height:100px; float:left; display:block; } .jwse_MyReviewTextRating { float: right; margin: 45px; } .jwse_MyReviewText{ margin-left:130px; background-color: var(--color-baseblue); color: white; font-size: 16px; padding:10px 30px; position:relative; border-radius:7px; } .jwse_MyReviewText:after { content: ''; position: absolute; left: 0; top: 30px; width: 0; height: 0; border: 20px solid transparent; border-right-color: var(--color-baseblue); border-left: 0; margin-top: -20px; margin-left: -20px; opacity:0.4; } .jwse_movieDescription { margin-top: 20px; display: block; font-size: 16px; font-weight: bold; margin-bottom: -10px; } .jwseHeroContainer { position: relative; top: 0px !important; } .ipsCarousel .ipsCarousel_inner { height: 230px !important; } #elMoviesFeatured .ipsCarousel .ipsCarousel_inner { height: 322px !important; } #elMoviesFeatured .ipsCarousel_item{ background-color:#FFF2CC; } #elMoviesFeatured .ipsButton { background-color: var(--color-baseblue) !important; color: white; }  
      I hope this will help get you started building your own Movie database. If you use this for your own website, please drop a link here, so we can see how it turned out. Also if you need help, just drop a comment here or in the forum and I will try my best to help you.
      Good luck and enjoy!

      How to make realistic estimates when working with software development

      Estimations for software development seem to be the bane of almost every company, but why is that really? Why is it so difficult to approximate the time and effort required when we do this all the time? There are a few reasons and in this article I will go over how I do my estimations and give you some of my experience on how you can maintain a 90% accuracy in any project.
      For a good estimate you need to start with one very important thing: define if you estimate based on time to complete or  time to deliver. These are very different because the time to deliver will be substantially higher and much more prone to variations. This is because in the daily work you will be dragged into meetings, have people distracting you and so on. For good estimations we always estimate on time to complete as our basis.
      Stop doing happy estimates
      The worst thing you can do is making happy estimates. These are estimates based on the utopia that everything will work smoothly and you will be left in total isolation to focus. It never happens and Murphy is always present. Happy estimates may seem good because the product owner love low estimates, but they hate when you fail to deliver.
      Happy estimates usually happen when an architect is stressed and throw out a guesstimate on the fly. It will almost always bite you in the butt because not only will everything seem easy to the architect, so they will underestimate it, they also never consider Murphy.
      You do not estimate to be nice, you estimate to give a realistic view on when something can be available in production. If you have a hard time to stop making happy estimates, then implement this very simple rule:
      "Any time that is needed beyond the time you have estimated you will do in your own time without payment"
      Take your time doing estimates
      An estimate is not a guesstimate that you throw out on a high level requirement. An estimate is your promise on when you can deliver and as such you should take your time and think that through. Don't sit around doing arbitrary guess work in a planning poker or play with t-shirt sizes unless you want to avoid doing any estimates at all. That type of guesstimating is for high level requirements, so make sure they stay there.
      Break down the requirement and use your experience to guide you to a first number in your estimation. This estimate is how long it will actually take to sit down and build based on the requirement. Make sure that you ask questions to clarify where needed so have the information you need. This is also a good time to start working on a solution design to make sure you have considered things that may not always be apparent. For example validation of data in integrations or extending a JavaScript validation for a new form.
      When we have done this, the number is still wrong, but we need it as a starting point.
      Add the things you forgot
      Now that you have done your estimate, many think they are done. That is rarely the case, so let us add the things that is not included yet. The most obvious problem that I see a lot is that the estimates is usually done by the most experience person, which also usually is the one that work fastest of everyone in the team. So what we do is to look at the estimate and the team, then we adjust based on what the slowest person in the team think is appropriate. This way we know that the base estimate is reasonable for everyone on the team.
      The second thing we do is to consider Murphy. Things always happen and based on the complexity we increase the estimate with 20-100%. Not having room for mistakes is in itself a very big mistake. It will almost always happen and if it does not happen for this particular task we will either be able to resolve things faster than expected, or we have more time for other tasks. Either way that is a positive thing.
      The third thing to look at are testing. All code should have unit tests and it is often forgotten in the estimate. You also have non-functional requirements such as loading times and browser support that you must consider. As a developer you are also responsible for testing the code and this should be added as a second estimate. For many estimates this alone will add 20-200% or even more to the estimate. This is especially true for frontend development where you often have 18+ variations just for devices and browsers.
      These three very simple things easily multiply the original estimate two to three times.
      Now let us consider your efficiency!
      A factor that is very often overlooked is efficiency. No person in any team will ever have 100% efficiency in the sprint. I do not mean your focus level during the day, even if that also is a factor, I am talking about those pesky things that break your concentration. Things like meetings, stand-ups, code reviews and all those "I just want to ask a quick question", cost a lot of your time during the day. In my experience most teams will have an efficiency of around 40-60% depending on how often they are disturbed.
      Depending on if your product owner understand estimation or not you can choose if you add the efficiency in the estimate, or if you have that as an external factor in the sprint itself. If you are new to this part of the estimation I suggest that you start with 50% efficiency and then adjust over time as you get better at estimating your efficiency.
      With this step we now add a second two time multiplier and you are now close to a realistic estimate.
      This may seem like a lot
      This may sound like the estimates always will be very high compared to how you are working today, and you are right. This is why you always feel stressed and why you fail to deliver on promise in every sprint. It is why we invent things like story points to mitigate our inability to take responsibility for things we can't estimate properly. Realistic estimates are just that and if your estimates are far lower than what you get doing estimates this way, then remember this very simple rule:
      It is always better to overestimate and over deliver than to underestimate and under deliver.
       
      The Quick Summary
      Make estimations in actual time to complete, not arbitrary measurements. Take your time to understand the task at hand and stop guesstimating. Adjust the estimate to fit the slowest person on your team. Add task for testing and make estimation separate for that. Remember that things always go wrong, so make room for that. Make sure that your efficiency is considered and calculated into the time to deliver.
      Making good estimates is based on experience and knowledge. This means that like other skills you can get better at it. If you constantly work with arbitrary measurements like story points and you constantly fail with no change to your estimation process, then you should stop doing that. Not only will you fail at a crucial part of your work, your inability to provide accurate estimates actually cause harm in the form of stress and frustration. It is up to you if you want to spend your time in constant failure or constantly provide estimates that are realistic and dependable.
      Learning to make good estimates is not rocket science, just common sense and experience.
      So start doing it today.

      Invision Community - building a blog from scratch

      This is a guide series that will go through everything you need to know to set up and customize your own blog using Invision Community from Invision Power Services. This guide will be updated with new articles or new information when new releases are made that affect the guides.
      This guide contains the following articles:
      Introduction (this page) Databases & Custom fields Adding Databases to Pages Adding CSS and JS to Pages Article View Template design Article Listing Template Design Article Category Listing Template Design Article Form Design Article Block Design Database Relationships This guide should give you all the information you need to get a good start with creating your own designs with Invision Community and its Pages application. If you want a quick start however and get a great looking design up and running in 10 minutes, then you can purchase a license for Invision Community and buy the plugin Pages SuperGrid by opentype.
      For this guide you will need a license for Pages, which is the application that allow you to work with Pages and Databases. I will make references to the Forum application as well, but you do not need that if you do not want to. The information in the articles will not go deep into how to make your blog compatible by using standard classes as that is a pretty big topic and I usually just build for myself, so I do not have to worry too much about that.
      If you have any questions or see a topic not yet added here, please drop by the forum and let me know.

      CodeLobster IDE - free PHP, HTML, CSS, JavaScript / TypeScript editor

      In this article, we suggest you to get acquainted with the free editor of web languages - CodeLobster IDE. It is presented on the software market for a long time already, and it wins a lot of fans.
       CodeLobster IDE allows you to edit PHP, HTML, CSS, JavaScript and TypeScript files, it highlights the syntax and gives hints for tags, functions and their parameters. This editor easily deals with those files that contain a mixed content.
       If you insert PHP code in your HTML template, then the editor correctly highlights both HTML tags and PHP functions. The same applies to CSS and JavaScript/TypeScript code, which is contained in HTML files.
       The program includes auto-completion function, which greatly speeds up the programmer's work and eliminates the possibility of errors.
       

       
      CodeLobster IDE provides contextual help on all supported programming languages, it uses the most up to date documentation at this moment, downloading it from official sites. So we can quickly get a description of any HTML tag, CSS attribute, PHP or JavaScript/TypeScript function by pressing the F1 key.
       The built-in PHP debugger allows you to execute PHP scripts step by step, sequentially moving through the lines of code. You can assign check points, view the process of the work of loops, and monitor the values of all variables during the execution of the script.
       Other useful functions and features of the IDE:
      A pair highlighting of parentheses and tags - you will never have to count parentheses or quotation marks, the editor will take care of it. Highlighting of blocks, selection and collapsing of code snippets, bookmarks to facilitate navigation on the edited file, recognition and building of the complete structure of PHP projects - these functions ensure easy work with projects of any scale.  Support for 17 user interface languages, among them English, German, Russian, Spanish, French and others. The program works on the following operation systems: Windows 7, Windows 8, Windows 10, Mac OS, Linux, Ubuntu, Fedora, Debian.  The professional version of CodeLobster IDE provides the programmer with even more features.
      For example, you have an opportunity to work with projects on a remote server with use of the built-in FTP client. You can edit the selected files, preview the results and then synchronize the changes with the files on the hosting.
      In addition the professional version includes an extensive set of plug-ins:
      Fully implemented support for JavaScript (and TypeScript) libraries, such as jQuery, Node.js, AngularJS, AngularTS, BackboneJS, EmberJS, VueJS and MeteorJS. A large set of extensions that help to work with PHP frameworks - CakePHP, CodeIgniter, Laravel, Phalcon, Smarty, Symfony, Twig and Yii plug-ins. Plugins for working with the most popular CMS - Drupal, Joomla, Magento and WordPress. Also CodeLobster IDE has special plug-in for Bootstrap.  
      We can download and install any framework directly from the program without being distracted from the main tasks.
       In general, for a year of work, our team had no complaints against the editor. CodeLobster IDE works fast, does not hang and allows us to work even with large PHP projects.
      You can download CodeLobster IDE from the original website:
      http://www.codelobster.com/
×
×
  • Create New...