PageSourceSearch

https://blender.community/c/rightclickselect/

html blender.community collected 2026-10-01 10:33:50 UTC 65,535 bytes, 1,405 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3  <head>
4
5
6    <title>Right-Click Select &mdash; 
7Blender Community
8    </title>
9    <meta name="theme-color" content="#f28128">
10
11<meta charset="utf-8"/>
12<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
13<meta name="description" content="Ideas for Blender
14"/>
15<meta name="keywords" content="animation,animate,2d,3d,2danimation,cg,motiongraphics,blender,greasepencil,toonboom,tvpaint,flash"/>
16
17<meta property="fb:app_id" content="384707122164385"/>
18<meta name="twitter:card" content="summary_large_image"/>
19<meta name="twitter:site" content="@BlenderToday"/>
20<meta property="og:site_name" content="Blender Community"/>
21<meta property="og:url" content="https://blender.community/c/rightclickselect/"/>
22<meta property="og:type" content="website"/>
23<meta name="twitter:title" content="Right-Click Select"/>
24<meta property="og:title" content="Right-Click Select"/>
25<meta name="twitter:description" content="Ideas for Blender
26"/>
27<meta property="og:description" content="Ideas for Blender
28"/>
29<meta name="twitter:image" content="https://blender.community/static/images/social.jpg"/>
30<meta name="twitter:image:alt" content="Right-Click Select"/>
31<meta property="og:image" content="https://blender.community/static/images/social.jpg"/>
32<meta property="og:image:width" content="1280"/>
33<meta property="og:image:height" content="720"/>
34
35
36<link rel="shortcut icon" type="image/jpg" href="https://d3a2gvihmbqfjo.cloudfront.net/cache/70/12/70129cd159e57d165040904b6cd7d089.png"/>
37
38
39
40<link href="https://blender.community/static/css/main.cc0c2e7493f8.css" rel="stylesheet" type="text/css" media="screen,projection" />
41
42
43<style>:root {
44  --community-theme-color: #f28128;
45}
46</style>
47
48
49  </head>
50  <body class="community-rightclickselect">
51
52
53    <div id="app" :class="{'is-entity-active': activeEntity, 'is-loading': isLoading}" class="app-container">
54      <modal-container @toggle-modal="toggleModal" v-if="activeModalContent" :modal-type="activeModalType" :modal-title="activeModalTitle" :modal-content="activeModalContent"></modal-container>
55      <nav class="app-nav">
56
57
58<ul class="communities">
59  <li class="c-main "><a data-tooltip="true" data-tippy-content="Home" data-tippy-placement="right" href="/" class="profile-avatar-container">
60      <div class="community-logo"><i class="i-home"></i>
61      </div></a><a href="/" class="community-name">Home</a>
62  </li>
63
64
65  <li class="c-rightclickselect is-active"><a data-tippy-content="Right-Click Select" data-tippy-placement="right" data-tooltip="true" href="/c/rightclickselect/" title="Right-Click Select" class="profile-avatar-container ">
66  <div class="profile-avatar avatar-size-medium"><img src="https://d3a2gvihmbqfjo.cloudfront.net/cache/70/12/70129cd159e57d165040904b6cd7d089.png" width="128" height="128" alt="Right-Click Select"/>
67  </div></a><a href="/c/rightclickselect/" class="community-name">Right-Click Select</a>
68
69  </li>
70  <li class="separator">
71    <hr/>
72  </li>
73  <li><a data-tooltip="true" data-tippy-content="All Communities" data-tippy-placement="right" href="/c/" class="profile-avatar-container">
74      <div class="community-logo"><img src="https://blender.community/static/favicon/favicon.58112853b96c.png" alt="All Communities"/>
75      </div></a><a href="/" class="community-name">All Communities</a>
76  </li>
77  <li class="c-about"><a data-tooltip="true" data-tippy-content="About" data-tippy-placement="right" href="/about/" class="profile-avatar-container">
78      <div class="profile-avatar avatar-size-medium"><i class="i-help"></i>
79      </div><a href="About" class="community-name">About</a></a>
80  </li>
81</ul>
82      </nav>
83
84
85      <main class="app-main">
86
87
88<header class="app-main-header">
89  <div v-if="isLoading" class="app-loader"></div>
90  <div class="header-title"><button class="btn-navbar-toggle js-navbar-toggle"><span><i class="i-menu"></i></span>
91</button><span><a data-tippy-content="Right-Click Select" data-tippy-placement="" data-tooltip="false" href="/c/rightclickselect/" title="Right-Click Select" class="profile-avatar-container ">
92  <div class="profile-avatar avatar-size-medium"><img src="https://d3a2gvihmbqfjo.cloudfront.net/cache/70/12/70129cd159e57d165040904b6cd7d089.png" width="128" height="128" alt="Right-Click Select"/>
93  </div></a></span><span><a href="/c/rightclickselect/" class="header-community-title">Right-Click Select</a></span>
94  </div>
95  <div v-if="isSearchEnabled" class="header-search">
96    <div class="form-group">
97      <input @keyup="setQueryText" @keydown.enter="getEntities" v-model="query.text" placeholder="Search..." class="form-control"/>
98      <div class="btn-toolbar-row">
99        <div class="btn-group">
100          <button @click="clearQueryText" v-if="query.text" class="btn-clear"><i class="i-cancel"></i>
101          </button>
102          <button @click="getEntities" class="btn-send"><i class="i-search"></i>
103          </button>
104        </div>
105      </div>
106    </div>
107  </div>
108  <div class="header-entity-detail-close">
109    <button class="btn-icon js-entity-detail-close"><span><i class="i-arrow-left"></i></span>
110    </button>
111  </div>
112  <div class="header-action-bar">
113    <div class="btn-toolbar-row">
114<div class="header-create-buttons"><a data-tooltip="true" data-tippy-content="Submit an Idea" href="/c/rightclickselect/create" class="btn-theme"><span><i class="i-idea"></i><span>Submit Idea</span></span></a>
115</div>
116
117
118<a href="/accounts/login/" title="Login with Blender ID" class="btn-link"><span><i class="i-log-in"></i><span>Login</span></span></a>
119    </div>
120  </div>
121</header>
122
123
124<div class="app-main-content">
125  <div class="app-feed">
126    <filter-sort-toolbar :community-categories="community.categories" :development-statuses="community.developmentStatuses" :sorting-orders="community.sortingOrders" :query="query" :query-extension="queryExtension" @set-query-category="setQueryCategory" @set-query-sorting="setQuerySorting" @set-query-development-status="setQueryDevelopmentStatus"></filter-sort-toolbar><ul v-if="entities && entities.length > 0" class="entities-list">
127  <entity-list-item v-for="entity in entities" v-bind:key="entity.id" v-bind:entity="entity" v-bind:active-entity="activeEntity" @set-active-entity="setActiveEntity"></entity-list-item>
128  <li v-if="nextPageNumber">
129    <div class="btn-load-more-container">
130      <button @click="appendEntitiesFromNextPage" :class="{'is-loading': isLoadingNextPage}" class="btn-wide"><span>Load More </span>
131      </button>
132    </div>
133  </li>
134</ul>
135<div v-else="v-else" class="entities-list-empty">No items to show.
136</div>
137  </div>
138  <div class="app-detail">
139    <div v-if="activeEntity">
140      <entity-detail @set-active-reply-form-comment-id="activeReplyFormCommentId = $event" @set-active-entity="setActiveEntity" @set-active-entity-reaction="setActiveEntityReaction" @set-active-entity-comments-reaction="setActiveEntityCommentsReaction" @append-entity-comments-from-next-page="appendEntityCommentsFromNextPage" @toggle-modal="toggleModal" @set-query-category="setQueryCategory" :active-reply-form-comment-id="activeReplyFormCommentId" :entity="activeEntity" :entity-type="'idea'" :comments="activeEntityComments" :current-user="currentUser" :url-api-comment-list-view-next-page="urlApiCommentListViewNextPage"></entity-detail>
141    </div>
142    <div v-else="v-else" class="community-detail">
143      <div class="community-detail-header">
144<a data-tippy-content="Right-Click Select" data-tippy-placement="" data-tooltip="false" href="/c/rightclickselect/" title="Right-Click Select" class="profile-avatar-container ">
145  <div class="profile-avatar avatar-size-large"><img src="https://d3a2gvihmbqfjo.cloudfront.net/cache/70/12/70129cd159e57d165040904b6cd7d089.png" width="128" height="128" alt="Right-Click Select"/>
146  </div></a>
147
148<div>
149  <h1 class="community-detail-title">Right-Click Select</h1>
150  <div class="community-detail-tagline">Ideas for Blender</div>
151  <div class="btn-toolbar-row">
152    <follow-toggle :url-follow-toggle="community.urlFollowToggle" :is-followed="community.isFollowed"></follow-toggle>
153<a data-tooltip="true" data-tippy-content="Website" href="https://chat.blender.org/#/room/#rightclickselect:blender.org" target="_blank" title="Website" class="btn-link btn-icon"><span><i class="i-link"></i></span></a>
154
155  </div>
156</div>
157      </div>
158      <div class="community-detail-content"><p>
159 Got an idea for a feature that you think
160 <a class="is-external" href="https://blender.org" rel="nofollow" target="_blank">
161  Blender
162 </a>
163 absolutely
164 <em>
165  must
166 </em>
167 have? This is the place to make your proposals. Share your idea with the community. Discuss it. Revise it. Flesh it out to the point that it's a well thought-out,
168 <em>
169  actionable
170 </em>
171 proposal that a Blender developer can work with.
172</p>
173<p>
174 Don't have any ideas right now? That's OK! Your experience counts. Help other people improve their ideas by voting and offering comments and insights.
175</p>
176
177
178      </div>
179    </div>
180
181<footer class="site-footer">
182  <div class="btn-toolbar-row"><a data-tooltip="true" data-tippy-content="Follow Blender Today on Twitter" href="https://twitter.com/BlenderToday" target="_blank" title="Follow Blender Today on Twitter" class="btn-link btn-icon"><span><i class="i-twitter"></i></span></a><a data-tooltip="true" data-tippy-content="Blender Community on YouTube" href="https://www.youtube.com/BlenderCommunity" target="_blank" title="Blender Community on YouTube" class="btn-link btn-icon"><span><i class="i-youtube"></i></span></a>
183  </div>
184  <div class="footer-nav">
185    <ul>
186      <li><a href="https://github.com/armadillica/dillo">give feedback</a>
187      </li>
188      <li><a href="/about/">about</a>
189      </li>
190      <li><a href="/privacy/">privacy</a>
191      </li>
192      <li><a href="/terms/">terms</a>
193      </li>
194    </ul>
195  </div>
196
197</footer>
198  </div>
199</div>
200
201      </main>
202    </div>
203
204
205
206<script type="text/javascript" src="https://blender.community/static/js/vendors.237e09e287be.js" charset="utf-8"></script>
206
207    
207<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
207
208    
208<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
208
209    
209<script type="text/javascript">axios.defaults.xsrfCookieName = 'csrftoken'
210axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
211    </script>
211
212
213    
213<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
vendor: 1 bytes, line 213
213
214<script type="text/javascript" src="https://blender.community/static/js/mixins.f1a22b079fd4.js" charset="utf-8"></script>
vendor: 1 bytes, line 214
214
215<script type="text/javascript" src="https://blender.community/static/js/filters.8a67c180bc87.js" charset="utf-8"></script>
215
216
217
218
219
220<script id="current-user-data" type="application/json">{"isAuthenticated": false, "isStaff": false, "isModerator": false, "username": null, "avatar": null, "url": null, "name": null, "notificationsCount": 0}</script>
220
221
222<script id="community-data" type="application/json">{"categories": [{"slug": "", "name": "All Categories"}, {"slug": "general", "name": "General"}, {"slug": "modeling", "name": "Modeling"}, {"slug": "sculpting", "name": "Sculpting"}, {"slug": "painting", "name": "Painting"}, {"slug": "particles", "name": "Particles"}, {"slug": "simulation", "name": "Simulation"}, {"slug": "animation", "name": "Animation"}, {"slug": "rigging", "name": "Rigging"}, {"slug": "rendering", "name": "Rendering"}, {"slug": "compositing", "name": "Compositing"}, {"slug": "motion-tracking", "name": "Motion Tracking"}, {"slug": "user-interface", "name": "User Interface"}, {"slug": "interoperability", "name": "Interoperability"}, {"slug": "game-engine", "name": "Game Engine"}, {"slug": "meta", "name": "Meta"}, {"slug": "scripting", "name": "Scripting"}, {"slug": "grease-pencil", "name": "Grease Pencil"}, {"slug": "sequencer", "name": "Video Sequencer"}, {"slug": "geometry-nodes", "name": "Geometry Nodes"}, {"slug": "materials-and-procedural-textures", "name": "Shading and Textures"}, {"slug": "uv", "name": "UV Editing"}, {"slug": "asset-browser", "name": "Asset Browser"}, {"slug": "text-editor", "name": "Text Editor"}, {"slug": "library-overrides", "name": "Library Overrides"}, {"slug": "outliner", "name": "Outliner"}, {"slug": "viewport", "name": "3D Viewport"}, {"slug": "benchmark", "name": "Benchmark"}, {"slug": "extensions", "name": "Extensions"}], "sortingOrders": [{"slug": "hot", "name": "Hot"}, {"slug": "new", "name": "New"}, {"slug": "top", "name": "Top"}], "slug": "rightclickselect", "activeEntityApiDetailViewUrl": null, "isFollowed": false, "name": "Right-Click Select", "urlFollowToggle": "/api/follow-toggle/15/30/", "urlApiEntityListView": "/api/rightclickselect/ideas/", "developmentStatuses": [{"slug": "", "name": "All Statuses"}, {"slug": "open", "name": "Open"}, {"slug": "in_development", "name": "In Development"}, {"slug": "done", "name": "Done"}, {"slug": "archived", "name": "Archived"}, {"slug": "incomplete", "name": "Incomplete"}]}</script>
222
223
224
225
226
227
228
229<script type="text/x-template" id="notifications-template">
230  <div><a @click.stop="toggleNotifications" :class="{'has-notifications': hasNotifications, 'is-active': isDropdownOpen}" :data-notifications-count="initialNotificationsCount" href="#" id="js-tippy-notifications" class="btn btn-icon btn-link notifications-toggle"><span><i class="i-bell"></i></span></a>
231    <div v-if="isDropdownOpen">
232      <div class="notifications-container">
233        <div class="notifications-header"><span>Activity</span><a @click.stop="markAllAsRead" href="#">Mark all as read</a>
234        </div>
235        <div class="notifications-content">
236          <div v-if="notifications && notifications.length == 0" class="notifications-empty">No notifications yet.
237          </div>
238          <ul v-if="notifications && notifications.length > 0">
239            <div v-for="notification in notifications" :class="'activity-verb-' + notification.verbSlug" class="activity-list-item"><a :href="notification.actionObjectUrl" class="activity-list-item-icon"><i :class="'i-' + notification.verbSlug"></i></a>
240              <div class="activity-list-item-content">
241                <div class="activity-list-item-header"><a :href="notification.actor.profileUrl" class="actor">{{ notification.actor.username }}
242</a><span>{{ notification.verb }}</span>
243                </div><a :href="notification.actionObjectUrl" :title="notification.actionObject" class="activity-list-item-action">{{ notification.actionObject }}
244</a><a :href="notification.actionObjectUrl" class="date">{{ notification.timeSince }}</a>
245              </div>
246            </div>
247          </ul>
248        </div>
249        <div v-if="urlNextPage" class="modal-action-container">
250          <button @click="loadNextPage" :class="{'is-loading': isLoadingNotifications}" class="btn-wide btn-secondary js-toggle-prevent"><span>Load More</span>
251          </button>
252        </div>
253      </div>
254    </div>
255  </div>
256</script>
vendor: 1 bytes, line 256
256
257<script type="text/javascript">Vue.component('notifications', {
258  mixins: [dropdownMixin],
259  template: '#notifications-template',
260  props: {
261    initialNotificationsCount: Number,
262    // Define the base url, queried everytime the dropdown is expanded
263    urlNotifications: String,
264    urlMarkAllAsRead: String,
265  },
266  data: function () {
267    return {
268      notifications: null,
269      urlNextPage: null,
270      notificationsCount: 0,
271      isLoadingNotifications: false,
272    }
273  },
274  computed: {
275    hasNotifications: function () {
276      return this.notificationsCount && this.notificationsCount > 0
277    }
278  },
279  methods: {
280    loadNotifications: function (urlNotifications, append) {
281      this.isLoadingNotifications = true;
282      axios
283        .get(urlNotifications)
284        .then(response => {
285          if (append === true) {
286            this.notifications.push(...response.data.results)
287          } else {
288            this.notifications = response.data.results;
289          }
290          this.urlNextPage = response.data.urlNextPage;
291
292          this.isLoadingNotifications = false;
293        })
294    },
295    toggleNotifications: function () {
296      if (this.isDropdownOpen) {
297        return this.hideDropdown()
298      }
299      this.loadNotifications(this.urlNotifications)
300      return this.showDropdown()
301    },
302    loadNextPage: function () {
303      this.loadNotifications(this.urlNextPage, true)
304    },
305    markAllAsRead: function () {
306      axios
307        .post(this.urlMarkAllAsRead)
308        .then(response => {
309          // Remove the notifications count from the page title.
310          if (this.notificationsCount > 0) {
311            document.title = document.title.split(") ").pop();
312          }
313
314          this.notificationsCount = 0;
315          Snackbar.show({text: 'All notifications marked as read!', icon: 'i-check', showAction: false, customClass: 'success'});
316        })
317    }
318  },
319  mounted () {
320    tippy('#js-tippy-notifications', {content: 'Notifications'})
321    this.notificationsCount = this.initialNotificationsCount;
322
323    // Append the notifications count to the page title.
324    if (this.notificationsCount > 0) {
325      document.title = '(' + this.notificationsCount + ') ' + document.title.replace(/ *\([^)]*\) */g, "");
326    }
327  }
328})
329</script>
329
330
331<script type="text/x-template" id="modal-container-template">
332  <div :class="'modal-type-' + modalType" role="dialog" tabindex="-1" class="modal-container">
333    <div @click="closeModal" class="modal-backdrop"></div>
334    <div role="document" class="modal-dialog">
335      <div class="modal-content">
336        <div class="modal-header">
337          <h2 class="modal-title">{{ modalTitle }}</h2>
338          <button @click="closeModal" aria-label="Close" type="button"><span aria-hidden="true"><i class="i-cancel"></i></span>
339          </button>
340        </div>
341        <modal-body-share v-if="modalType === 'share'" :absoluteUrl="modalContent.absoluteUrl"></modal-body-share>
342        <modal-body-generic-form v-if="modalType === 'generic_form'" :urlForm="modalContent.urlForm"></modal-body-generic-form>
343        <modal-body-user-list v-if="modalType === 'userList'" :urlUserList="modalContent.urlUserList"></modal-body-user-list>
344      </div>
345    </div>
346  </div>
347</script>
vendor: 1 bytes, line 347
347
348<script type="application/javascript">Vue.component('modal-container', {
349  template: '#modal-container-template',
350  props: {modalType: String, modalTitle: String, modalContent: Object },
351  methods: {
352    closeModal: function() {
353      this.$emit('toggle-modal')
354    }
355  }
356})
357</script>
357
358
359<script type="text/x-template" id="modal-body-share-template">
360  <div class="modal-body">
361    <div class="btn-toolbar-row"><a :href="'https://www.facebook.com/sharer/sharer.php?u=' + absoluteUrlWithDomain" title="Share on Facebook" class="btn-icon btn-social-facebook"><span><i class="i-facebook"></i></span></a><a :href="'https://twitter.com/intent/tweet?text=' + absoluteUrlWithDomain" title="Share on Twitter" class="btn-icon btn-social-twitter"><span><i class="i-twitter"></i></span></a><a :href="'https://www.linkedin.com/sharing/share-offsite/?url=' + absoluteUrlWithDomain" title="Share on LinkedIn" class="btn-icon btn-social-linkedin"><span><i class="i-linkedin"></i></span></a>
362    </div>
363  </div>
364</script>
vendor: 1 bytes, line 364
364
365<script type="application/javascript">Vue.component('modal-body-share', {
366  template: '#modal-body-share-template',
367  props: {absoluteUrl: String },
368  computed: {
369    absoluteUrlWithDomain: function () {
370      return window.location.href;
371    }
372  },
373})
374</script>
374
375
376<script type="text/x-template" id="modal-body-generic-form-template">
377  <div v-html="formHtml" class="modal-body"></div>
378</script>
vendor: 1 bytes, line 378
378
379<script type="application/javascript">Vue.component('modal-body-generic-form', {
380  template: '#modal-body-generic-form-template',
381  props: {urlForm: String },
382  data: function () {
383    return {
384      formHtml: null
385    }
386  },
387  mounted: function (){
388    axios
389      .get(this.urlForm)
390      .then(response => {
391        this.formHtml = response.data;
392      })
393      .then(() => {
394        let form = document.getElementById("generic_form");
395        form.addEventListener("submit", this.submitForm);
396      });
397  },
398  methods: {
399    submitForm: function (event) {
400      event.preventDefault();
401      const formData = new FormData(event.target)
402      const params = new URLSearchParams();
403      for (let [key, val] of formData.entries()) {
404        params.set(key, val)
405      }
406      const config = {
407        headers: {
408          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
409        }
410      }
411      axios.post(this.urlForm, params, config)
412        .then(response => {this.formHtml = response.data;})
413        .then(() => {
414          let form = document.getElementById("generic_form");
415          form.addEventListener("submit", this.submitForm);
416        })
417    }
418  },
419})
420</script>
420
421
422<script type="text/x-template" id="modal-body-user-list-template">
423  <div>
424    <div class="modal-body">
425      <div v-if="isLoading" class="modal-loading">Loading...</div>
426      <div v-if="! isLoading && userList.length === 0" class="modal-loading">No votes found</div>
427      <ul>
428        <li v-for="(user, index) in userList">
429          <div class="profile-list-item">
430            <user-avatar :size="'medium'" :user="user"></user-avatar><a :href="user.url" class="profile-list-info">
431              <div class="profile-list-title"><span>{{ user.name }}</span>
432              </div>
433              <div class="profile-list-subtitle"><span>{{ user.username }}</span>
434              </div></a>
435          </div>
436        </li>
437      </ul>
438    </div>
439    <div v-if="urlNextPage" class="modal-action-container">
440      <button @click="loadNextPage" class="btn-wide btn-secondary"><span>Load More</span>
441      </button>
442    </div>
443  </div>
444</script>
vendor: 1 bytes, line 444
444
445<script type="application/javascript">Vue.component('modal-body-user-list', {
446  template: '#modal-body-user-list-template',
447  props: {urlUserList: String },
448  data: function () {
449    return {
450      isLoading: true,
451      userList: [],
452      urlNextPage: null
453    }
454  },
455  mounted: function (){
456    this.loadUserList(this.urlUserList);
457  },
458  methods: {
459    loadUserList: function (urlUserList, append) {
460      axios
461        .get(urlUserList)
462        .then(response => {
463          this.isLoading = false;
464          if (append === true) {
465            this.userList.push(...response.data.results)
466          } else {
467            this.userList = response.data.results;
468          }
469          this.urlNextPage = response.data.urlNextPage;
470        })
471    },
472    loadNextPage: function () {
473      this.loadUserList(this.urlNextPage, true);
474    }
475  }
476})
477</script>
477
478
479
480<script type="text/x-template" id="user-avatar-template"><a :href="user.url" :title="user.username" data-placement="" data-toggle="" class="profile-avatar-container">
481    <div :class="sizeClass" class="profile-avatar"><img v-if="user.avatar" :alt="user.username" :src="user.avatar" height="128" width="128"/><i v-else="v-else" class="i-user"></i>
482    </div></a>
483</script>
vendor: 1 bytes, line 483
483
484<script>Vue.component('user-avatar', {
485  template: '#user-avatar-template',
486  props: {user: Object, size: String},
487
488  computed: {
489    sizeClass: function () {
490      let size = this.size ? this.size : 'medium';
491      return 'avatar-size-' + size;
492    }
493  },
494})
495</script>
495
496
497<script type="text/x-template" id="reactions-template">
498  <div v-if="widgetType == 'icon'" class="btn-group">
499    <div @click="likeToggle" :class="{'is-liked': isLiked}" data-toggle="tooltip" title="Like this comment" class="btn-like"><span><i :class="[isLiked ? 'i-heart-filled' : 'i-heart']"></i></span>
500    </div>
501    <div v-if="likesCount" @click="toggleModalUserListLiked" :class="{'is-liked': isLiked}" data-toggle="tooltip" title="See who liked this comment" class="btn-like"><span>{{ likesCount }}</span>
502    </div>
503  </div>
504  <div v-else-if="widgetType == 'button'" class="btn-group">
505    <div @click="likeToggle" :class="{'is-liked': isLiked}" title="Like this post" class="btn-secondary btn-icon"><span><i :class="[isLiked ? 'i-heart-filled' : 'i-heart']"></i><span v-if="!likesCount">Like</span></span>
506    </div>
507    <div v-if="likesCount" @click="toggleModalUserListLiked" :class="{'is-liked': isLiked}" title="See who liked this post" class="btn-secondary"><span v-if="likesCount">{{ likesCount }}</span><span v-else="v-else" @click="likeToggle">Like</span>
508    </div>
509  </div>
510</script>
vendor: 1 bytes, line 510
510
511<script type="text/javascript">Vue.component('reactions', {
512  template: '#reactions-template',
513  props: {
514    // Affect display of the component (either a simple icon, or a button)
515    widgetType: {
516      type: String,
517      default: 'icon',
518      validator: function (value) {
519        // The value must match one of these strings
520        return ['icon', 'button'].indexOf(value) !== -1
521      }
522    },
523    currentUser: {
524      type: Object,
525      required: true,
526      validator: function (user) {
527        const expectedProps = ['isAuthenticated', 'username', 'name', 'avatar', 'url'];
528        return expectedProps.every(item => user.hasOwnProperty(item));
529      }
530    },
531    commentId: Number,
532    likesCount: Number,
533    isLiked: Boolean,
534    isOwn: Boolean,
535    likeToggleUrl: String,
536    entityUrlApiDetailView: String,
537    urlUserListLiked: String
538  },
539  methods: {
540    likeToggle: function() {
541      // Ensure the user is authenticated, otherwise redirect to login
542      // TODO Add next url parameter
543      if (! this.currentUser.isAuthenticated) {
544        window.location.href = "/accounts/login/";
545        return;
546      }
547
548      // Prevent own likes.
549      if (!this.isLiked && this.isOwn) {
550        Snackbar.show({text: "Don't we all like our own stuff?", icon: 'i-happy', showAction: false, customClass: 'warning'})
551        return;
552      }
553
554      // Fake interactivity
555      // Update like/unlike toggle before doing the request. If the request is
556      // unsuccessful, revert to the original values and display an error.
557      let isLiked = !this.isLiked;
558      // Make a deep copy of likesCount so it can be manipulated
559      let likesCount = JSON.parse(JSON.stringify(this.likesCount));
560      if (isLiked) {
561        likesCount++;
562      } else {
563        likesCount--;
564      }
565
566      // If commentId is defined generate events related to comments,
567      // otherwise generate events related to entities.
568      if (this.commentId) {
569        this.$emit('set-active-entity-comments-reaction',
570          {commentId: this.commentId, isLiked: isLiked, likesCount: likesCount});
571      } else {
572        this.$emit('set-active-entity-reaction',
573          {isLiked: isLiked, likesCount: likesCount});
574      }
575      const params = new URLSearchParams();
576      const config = {
577        headers: {
578          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
579        }
580      }
581      axios
582        .post(this.likeToggleUrl, params, config)
583        .then(response => {
584          // Uncomment this to update component data after the API request has
585          // succeeded
586          // this.likesCountData = response.data.likes_count;
587          // this.isLikedData = response.data.action === 'liked';
588          // Uncomment this to trigger a full entity reload
589          // this.$emit('set-active-entity', this.entityUrlApiDetailView)
590        }).catch(error => {
591          if (error.response) {
592            Snackbar.show({text: error.response.data, icon: 'i-alert-triangle', showAction: false, customClass: 'error'})
593          }
594      });
595    },
596    toggleModalUserListLiked: function () {
597      this.$emit('toggle-modal', {
598          type: 'userList',
599          title: 'Who liked this',
600          content: {'urlUserList': this.urlUserListLiked}
601        }
602      )
603    }
604  }
605})
606</script>
606
607
608<script type="text/x-template" id="follow-toggle-template"><a @click.prevent.stop="followToggle" :href="urlFollowToggle" :class="[isFollowedData ? 'btn-secondary' : 'btn-theme-primary']" :data-tippy-content="[isFollowedData ? 'Stop following this community' : 'Follow this community']" data-tooltip="true"><span><i :class="[isFollowedData ? 'i-user-minus' : 'i-user-plus']"></i><span v-if="isFollowedData">Unfollow</span><span v-else="v-else">Follow</span></span></a>
609</script>
vendor: 1 bytes, line 609
609
610<script type="application/javascript">Vue.component('follow-toggle', {
611template: '#follow-toggle-template',
612props: {urlFollowToggle: String, isFollowed: Boolean},
613data: function () {
614  return {
615    isFollowedData: null,
616  }
617},
618mounted() {
619  this.isFollowedData = this.isFollowed
620},
621methods: {
622  followToggle: function() {
623    axios
624      .get(this.urlFollowToggle)
625      .then(response => {
626        this.isFollowedData = !this.isFollowedData
627      })
628    }
629  }
630})
631</script>
631
632
633<script type="text/x-template" id="entity-list-item-template">
634  <li @click="toggleActive" :class="[entity.isHot ? 'is-hot' : '', isActive ? 'is-active' : '', entity.isPinnedByModerator ? 'is-pinned' : '', entity.votes ? 'idea-status-' + [entity.developmentStatus] : '']" class="entity-list-item"><a v-if="!entity.votes && !entity.operatingSystem" :href="entity.absoluteUrl" :title="entity.categories" class="entity-list-item-icon"><i :class="['i-' + [entity.categories]]"></i></a><a v-if="entity.votes" :href="entity.absoluteUrl" class="entity-list-item-icon">
635      <div :style="'background-size: 100% ' + [entity.votes.positivePercentage] + '%'" :title="[[entity.votes.positivePercentage] + '% Positive']" class="idea-percent-widget"><i class="i-idea"></i>
636      </div></a><a v-if="entity.operatingSystem" :href="entity.absoluteUrl" :title="entity.operatingSystem.name" class="entity-list-item-icon"><i :class="['i-' + [entity.operatingSystem.slug]]"></i></a>
637    <div class="entity-list-item-content"><a :href="entity.absoluteUrl" class="entity-list-item-title">{{ entity.title }}
638</a>
639      <div class="entity-list-item-meta">
640        <ul class="entity-list-item-meta-categories">
641          <li v-if="entity.operatingSystem" :title="entity.operatingSystem.name" class="item-os"><strong>{{ entity.operatingSystem.name }}</strong>
642          </li>
643
644          <li v-if="entity.categories != 0" class="item-categories"><span :title="entity.categories" class="category">{{ entity.categories }}</span>
645          </li>
646        </ul>
647        <ul class="entity-list-item-meta-info">
648          <li v-if="entity.isLink"><a :href="entity.linkURL" target="_blank" title="External link">{{ entity.linkHostname }}
649</a>
650          </li>
651          <li v-if="entity.developmentStatus && entity.developmentStatus != 'open'" title="Development Status" class="idea-dev-status"><strong>{{ entity.developmentStatus | titleize }}</strong>
652          </li>
653
654          <li v-if="entity.commentsCount != 0" title="Comments"><i class="i-comment"></i>{{ entity.commentsCount }}
655
656          </li>
657          <li v-if="entity.likesCount != 0" title="Likes"><i class="i-heart"></i>{{ entity.likesCount }}
658
659          </li>
660          <li class="item-date"><a :href="entity.absoluteUrl" :title="entity.datePublished"><span>{{ entity.naturalDatePublished }}</span></a>
661          </li>
662          <li v-if="entity.isHot" title="Hot stuff!"><i class="i-fire"></i>
663          </li>
664          <li v-if="entity.votes" :title="entity.votes.positivePercentage + '% positive'"><i class="i-arrow-up"></i>{{ entity.votesPositiveCount }} &nbsp;
665<i class="i-arrow-down"></i>{{ entity.votesNegativeCount }}
666
667          </li>
668          <li v-if="entity.downloadsCount && entity.downloadsCount != 0" title="Downloads"><i class="i-download"></i>{{ entity.downloadsCount }}
669
670          </li>
671          <li v-if="entity.isPinnedByModerator" title="Pinned"><i title="Pinned" class="i-pin"></i>
672          </li>
673        </ul>
674      </div>
675    </div>
676  </li>
677</script>
vendor: 1 bytes, line 677
677
678<script type="application/javascript">Vue.component('entity-list-item', {
679  template: '#entity-list-item-template',
680  props: {entity: Object, activeEntity: Object },
681
682  computed: {
683    isActive: function () {
684      if (!this.activeEntity) return false;
685      return this.entity.urlApiDetailView === this.activeEntity.urlApiDetailView;
686    }
687  },
688
689  methods: {
690    toggleActive: function(e) {
691      e.preventDefault();
692      this.$emit('set-active-entity', this.entity.urlApiDetailView);
693
694      let app = document.getElementById('app');
695      app.classList.add('is-entity-active');
696    }
697  }
698})
699</script>
699
700
701
702<script type="text/x-template" id="entity-detail-template">
703  <div id="entity-detail">
704    <div id="modal-media" @click="dismissMedia" v-if="expandedMedia" class="modal-container">
705      <div class="media"><img :src="expandedMedia.src" :alt="expandedMedia.alt"/>
706      </div>
707      <div class="modal-backdrop"></div>
708    </div>
709    <div :class="[
710        entity.isLink ? 'is-link' : 'is-text',
711        entity.isHot ? 'is-hot' : '',
712        entity.linkIsOembed ? 'is-oembed' : '',
713        'is-type-' + entityType,
714      ]" class="entity-detail-item">
715      <div class="entity-detail-item-header">
716        <div class="entity-detail-item-meta">
717          <ul v-if="entity.categories.length > 0" class="entity-detail-item-tags">
718            <li v-for="(category, index) in entity.categories"><span v-if="index != 0">,&nbsp;</span><a :href="entity.absoluteUrl + '?category=' + category[1]" @click.prevent.stop="$emit('set-query-category', category[1])"><span :title="category[0]">{{ category[0] }}</span></a>
719            </li>
720          </ul>
721          <ul v-else="v-else" class="entity-detail-item-tags">
722            <li>Categories</li>
723          </ul>
724          <ul>
725            <li v-if="entity.isEditable && !isReadOnly"><a :href="entity.editUrl" title="Edit Post">Edit</a>
726            </li>
727            <li v-if="entity.likesCount > 0"><a @click="toggleModalUserListLiked" href="#" title="Likes"><span><i :class="[entity.isHot ? 'i-fire' : 'i-heart']"></i>{{ entity.likesCount }}
728</span></a>
729            </li>
730            <li v-if="entity.status != 'published'">
731              <div :class="'badge-' + entity.status" class="badge-pill">{{ entity.status }}</div>
732            </li>
733            <li><a v-if="!isReadOnly && entity.naturalDatePublished" :href="entity.absoluteUrl" :title="'Published on ' + entity.datePublished">Published {{ entity.naturalDatePublished }} ago</a>
734            </li>
735            <li v-if="entity.isEdited"><span :title="'Edited on ' + entity.dateUpdated">Edited {{ entity.naturalDateUpdated }} ago</span>
736            </li>
737
738            <li v-if="entity.isPinnedByModerator"><i title="Pinned Post" class="i-pin"></i>
739            </li>
740          </ul>
741        </div><a v-if="entity.title && entity.isLink" :href="entity.linkURL" rel="nofollow" target="_blank" class="entity-detail-item-title">{{ entity.title }}</a>
742        <div v-else-if="entity.title" class="entity-detail-item-title">{{ entity.title }}
743        </div>
744        <div v-else="v-else" class="entity-detail-item-title">Future Masterpiece
745        </div>
746      </div>
747
748      <div v-if="entity.downloadable" class="entity-summary-container downloadable"><a v-if="entity.downloadable.url" :href="entity.downloadable.url" class="entity-summary-icon"><i :class="['i-' + [entity.operatingSystem.slug]]"></i></a>
749        <div v-if="entity.downloadable.url" class="downloadable-meta-container">
750          <div class="downloadable-meta">
751            <h3>{{ entity.operatingSystem.name }} Build</h3>
752            <ul>
753              <li v-if="entity.downloadable.sizeHuman">{{ entity.downloadable.sizeHuman }}</li>
754              <li v-if="entity.downloadable.count && entity.downloadable.count != 0" :title="entity.downloadable.count + ' downloads'"><i class="i-download"></i>{{ entity.downloadable.countCompact }}
755
756              </li>
757            </ul>
758          </div>
759          <div class="btn-toolbar-row"><a :href="entity.downloadable.url" class="btn-theme-primary"><span><i class="i-download"></i><span>Download</span></span></a>
760          </div>
761        </div>
762        <div v-else="v-else" class="downloadable-meta-container error">
763          <p>There is a problem with this build.</p>
764          <p>Please <a href="https://github.com/armadillica/dillo/issues">report the issue</a>.
765          </p>
766        </div>
767      </div>
768
769      <div v-if="entity.content && entity.isLink && entity.linkIsOembed" v-html="entity.content" class="entity-content"></div><a v-else-if="entity.content && entity.isLink" :href="entity.linkURL" :title="entity.linkURL" rel="nofollow" target="_blank" class="entity-link-content">
770        <div class="entity-link-url">
771          <div><img :src="entity.linkFavicon" alt="Link favicon"/>
772          </div>
773          <div>{{ entity.linkURL }}</div>
774        </div>
775        <button class="btn-theme"><span><i class="i-link"></i><span>Open Link</span></span>
776        </button></a>
777      <div v-else-if="entity.content" v-html="entity.content" class="entity-content"></div>
778      <div v-if="entity.votes" class="entity-summary-container idea">
779        <div class="idea-percent-widget-container">
780          <div :style="'background-size: 100% ' + [entity.votes.positivePercentage] + '%'" :title="[[entity.votes.positivePercentage] + '% Positive']" class="idea-percent-widget"><i class="i-idea"></i>
781          </div>
782          <div class="percentage">{{ entity.votes.positivePercentage }}%</div>
783        </div>
784        <div class="idea-action-buttons-container">
785          <h3 v-if="entity.developmentStatus" class="status">Development Status:&nbsp;<span :class="['dev-status-' + [entity.developmentStatus]]">{{ entity.developmentStatus | titleize }}
786<i v-if="entity.developmentStatus == 'done'" class="i-check"></i></span>
787          </h3>
788          <div class="btn-toolbar-row">
789            <vote v-if="!isReadOnly" v-on="$listeners" :current-user="currentUser" :direction="'up'" :entity="entity"></vote>
790            <vote v-if="!isReadOnly" v-on="$listeners" :current-user="currentUser" :direction="'down'" :entity="entity"></vote><a v-if="entity.developmentUrl" :href="entity.developmentUrl" target="_blank" title="Development URL" class="btn btn-theme"><span><i class="i-code"></i><span>{{ entity.developmentUrlLabel }}</span></span></a>
791            <div v-if="(entity.isOwn || currentUser.isModerator) && !isReadOnly && !entity.isDuplicateOf && entity.duplicates.length === 0" @click="toggleModalDuplicateOf" class="btn btn-secondary"><span><i class="i-copy"></i><span>Mark Duplicate</span></span>
792            </div>
793          </div>
794          <div v-if="entity.isDuplicateOf" class="idea-duplicates">
795            <p>This idea has been marked as duplicate of <a :href="entity.isDuplicateOf.url">{{ entity.isDuplicateOf.title }}</a>.
796            </p>
797            <button v-if="(entity.isOwn || currentUser.isModerator) && !isReadOnly" @click="duplicateOfDelete" class="btn-secondary"><span><i class="i-cancel"></i><span>Unmark Duplicate</span></span>
798            </button>
799          </div>
800          <div v-if="entity.duplicates.length > 0" class="idea-duplicates">
801            <p><i class="i-copy"></i> This idea is popular! It has {{ entity.duplicates.length }} {{ entity.duplicates.length | pluralize('duplicate') }}.
802            </p>
803            <ul>
804              <li v-for="duplicate in entity.duplicates"><span class="meta"><i title="Upvotes" class="i-arrow-up"></i>{{ duplicate.votes.positiveCount }}
805<i title="Downvotes" class="i-arrow-down"></i>{{ duplicate.votes.negativeCount }}</span><a :href="duplicate.url">{{ duplicate.title }}</a>
806              </li>
807            </ul>
808          </div>
809        </div>
810      </div>
811      <div class="entity-detail-info-container">
812        <div class="entity-detail-user-info">
813          <user-avatar :user="entity.user"></user-avatar>
814          <div class="user-info"><a :href="entity.user.url" :title="entity.user.username" class="username"><span v-if="entity.user.name">{{ entity.user.name }}</span><span v-else="v-else">{{ entity.user.username }}</span></a>
815            <div class="user-info-meta">
816              <div v-if="entity.user.badges && entity.user.badges.length > 0" class="badges-row">
817                <div v-for="badge in entity.user.badges" :title="badge.name" class="badge is-user"><img :src="badge.urlImage" :alt="badge.name"/>
818                </div>
819              </div>
820              <div v-if="entity.user.likes_count" title="Karma" class="user-rating"><i class="i-heart"></i>{{ entity.user.likes_count }}
821              </div>
822            </div>
823          </div>
824        </div>
825
826        <div class="entity-detail-reactions-container">
827          <div v-if="!isReadOnly" class="btn-toolbar-row right"><a v-if="entity.isEditable" :href="entity.editUrl" title="Edit Post" class="btn-icon btn-secondary"><span><i class="i-edit"></i><span>Edit</span></span></a>
828            <reactions v-on="$listeners" :widget-type="'button'" :current-user="currentUser" :is-liked="entity.isLiked" :is-own="entity.isOwn" :like-toggle-url="entity.likeToggleUrl" :likes-count="entity.likesCount" :entity-url-api-detail-view="entity.urlApiDetailVie
828w" :url-user-list-liked="entity.urlUserListLiked"></reactions><a @click="toggleModalShare" title="Share Post" class="btn-icon btn-secondary"><span><i class="i-share"></i></span></a>
829            <div v-if="currentUser.isAuthenticated" class="dropdown"><a @click="toggleDropdown" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-secondary btn-icon"><span><i class="i-more-vertical"></i></span></a>
830              <div :class="{ show: isDropdownOpen }" class="dropdown-menu dropdown-menu-right"><a href="#" @click="toggleModalGenericForm(entity.urlReport)" title="Report Post" class="dropdown-item"><span><i class="i-flag"></i>Report Post...</span></a>
831                <div v-if="currentUser.isModerator" class="dropdown-divider"></div><a v-if="currentUser.isModerator" href="#" @click="toggleModalGenericForm(entity.user.urlRemoveSpam)" title="Remove Spam" class="dropdown-item danger"><span><i class="i-trash"></i>Remove Spam...</span></a>
832                <div v-if="currentUser.isStaff" class="dropdown-divider"></div><a v-if="currentUser.isStaff" :href="entity.urlEditAdmin" data-toggle="tooltip" title="Edit in Admin" class="dropdown-item is-admin"><span><i class="i-coffee"></i>Admin</span></a>
833              </div>
834            </div>
835          </div>
836        </div>
837      </div>
838    </div>
839    <div v-if="!isReadOnly" class="comments-list-container">
840      <comment-form v-if="!isReadOnly" v-on="$listeners" :entity="entity" :current-user="currentUser"></comment-form>
841      <div v-if="comments && comments.length" class="comments-list-header">{{ entity.commentsCount }} Comment<span v-if="entity.commentsCount > 1">s</span>
842      </div>
843      <comment-list v-if="!isReadOnly" v-on="$listeners" :active-reply-form-comment-id="activeReplyFormCommentId" :entity="entity" :comments="comments" :url-api-comment-list-view-next-page="urlApiCommentListViewNextPage" :current-user="currentUser"></comment-list>
844    </div>
845  </div>
846</script>
vendor: 1 bytes, line 846
846
847<script type="application/javascript">Vue.component('entity-detail', {
848  mixins: [dropdownMixin],
849  template: '#entity-detail-template',
850  data: function () {
851    return {
852      expandedMedia: null,
853    }
854  },
855  props: {
856    entity: Object,
857    entityType: {
858      type: String,
859      validator: function (value) {
860        // The value must match one of these strings
861        return ['post', 'idea', 'build'].indexOf(value) !== -1
862      }
863    },
864    comments: Array,
865    urlApiCommentListViewNextPage: String,
866    currentUser: Object,
867    activeReplyFormCommentId: Number,
868    isReadOnly: false, // Set to true when using the component to preview a post in edit mode
869    },
870  updated: function (val, oldVal) {
871    // Look for images with js-media-expand and allow them to expandMedia
872    let elements = document.getElementsByClassName('js-media-expand');
873    for (var i = 0; i < elements.length; i++) {
874      elements[i].addEventListener('click', this.expandMedia, false);
875    }
876  },
877  mounted() {
878    // Close modal when pressing Esc.
879    document.body.addEventListener('keyup', e => {
880      if (e.keyCode === 27) { this.dismissMedia();}
881    })
882  },
883  methods: {
884    expandMedia: function (event) {
885      document.body.classList.add('is-modal-open');
886      this.expandedMedia = {src: event.target.src, alt: event.target.alt}
887    },
888    dismissMedia: function () {
889      document.body.classList.remove('is-modal-open');
890      this.expandedMedia = null;
891    },
892    toggleModalShare: function () {
893      if (navigator.share) {
894        navigator.share({
895          title: this.entity.title,
896          text: this.entity.content,
897          url: this.entity.absoluteUrl,
898        })
899          .then(() => console.log('Success!'))
900          .catch((error) => console.log('Error', error));
901      } else {
902        this.$emit('toggle-modal', {
903          type: 'share',
904          title: 'Share',
905          content: {'absoluteUrl': this.entity.absoluteUrl}}
906        )
907      }
908    },
909    toggleModalGenericForm: function (urlForm) {
910      this.$emit('toggle-modal', {
911        type: 'generic_form',
912        title: 'Report',
913        content: {'urlForm': urlForm}}
914      )
915    },
916    toggleModalUserListLiked: function () {
917      this.$emit('toggle-modal', {
918        type: 'userList',
919        title: 'Who liked this',
920        content: {'urlUserList': this.entity.urlUserListLiked}}
921      )
922    },
923    // RCS Idea Entity method
924    toggleModalDuplicateOf: function () {
925      this.$emit('toggle-modal', {
926          type: 'generic_form',
927          title: 'This is a duplicate of...',
928          content: {'urlForm': this.entity.urlDuplicateOf}
929        }
930      )
931    },
932    // RCS Idea Entity method
933    duplicateOfDelete: function () {
934      if(confirm('Unmark duplicate?'))
935        axios
936          .post(this.entity.urlDuplicateOfDelete)
937          .then(response => {
938            this.$emit('set-active-entity', this.entity.urlApiDetailView)
939          })
940          .catch(error => {
941            console.log('Error! Could not reach the API. ' + error)
942          })
943    },
944  }
945})
946</script>
946
947
948<script type="text/x-template" id="comment-form-template">
949  <div class="comments-container">
950    <div v-if="currentUser.isAuthenticated" class="comment-item comment-form-container">
951      <div class="comment-avatar">
952        <user-avatar :size="'small'" :user="currentUser"></user-avatar>
953      </div>
954      <div class="comment-body">
955        <div class="comment-form">
956          <div class="textarea-wrap js-textarea-wrap"><textarea @keydown.ctrl.enter="postComment" @keydown.meta.enter="postComment" @keydown.ctrl.b="setMarkdownStyle" @keydown.meta.b="setMarkdownStyle" @keydown.ctrl.i="setMarkdownStyle" @keydown.meta.i="setMarkdownStyle" v-model="message" onInput="this.parentNode.dataset.replicatedValue = this.value" placeholder="Write a comment..." rows="2" class="form-control"></textarea>
957          </div>
958          <div class="btn-toolbar-row">
959            <button @click.stop="cancelComment" data-toggle="tooltip" title="Cancel" class="btn btn-comment-cancel"><span><i class="i-cancel"></i></span>
960            </button>
961            <button @click="postComment" :class="{'is-loading': isLoading}" title="Send Comment" class="btn-comment"><span>Comment</span>
962            </button>
963          </div>
964          <div v-html="messagePreview" :class="{'is-loading': isLoadingPreview}" class="comment-preview comment-content js-comment-preview"></div>
965        </div>
966      </div>
967    </div>
968    <div v-else="v-else" class="comment-form-login"><a href="/accounts/login" title="Login with Blender ID" class="btn-secondary btn-wide"><span><i class="i-log-in"></i><span>Login to Comment</span></span></a>
969    </div>
970  </div>
971</script>
vendor: 1 bytes, line 971
971
972<script type="application/javascript">Vue.component('comment-form', {
973  template: '#comment-form-template',
974  props: {
975    entity: Object,
976    parentCommentId: Number,
977    currentUser: Object
978  },
979  data: function () {
980    return {
981      message: this.message,
982      messagePreview: null,
983      isLoading: false,
984      isLoadingPreview: false,
985    }
986  },
987  watch: {
988    // whenever question changes, this function will run
989    message: function (newMessage, oldMessage) {
990      this.debouncedPreviewCommentContent()
991    }
992  },
993  created: function () {
994    this.debouncedPreviewCommentContent = _.debounce(this.previewCommentContent, 500)
995  },
996  methods: {
997    previewCommentContent: function () {
998      this.isLoadingPreview = true;
999      const params = new URLSearchParams({'content': this.message});
1000      axios
1001        .post('/api/markdown-preview/', params)
1002        .then(response => {
1003            this.messagePreview = response.data.preview;
1004            this.isLoadingPreview = false;
1005        })
1006        .catch(error => {
1007          this.messagePreview = 'Error! Could not reach the API. ' + error
1008        })
1009
1010    },
1011    cancelComment: function(){
1012      if(confirm('Do you want to cancel the reply?'))
1013        this.$emit('set-active-reply-form-comment-id', null)
1014    },
1015    postComment: function() {
1016      if (!this.message){
1017        Snackbar.show({text: 'Try writing something.', icon: 'i-alert-triangle', showAction: false, customClass: 'error'});
1018        return;
1019      }
1020
1021      this.isLoading = true;
1022
1023      //if (this.message.length < 5) return;
1024      const params = new URLSearchParams();
1025      params.append('content', this.message);
1026      params.append('entity_object_id', this.entity.objectId);
1027      params.append('entity_content_type_id', this.entity.contentTypeId);
1028      if (this.parentCommentId) {
1029        params.append('parent_comment_id', this.parentCommentId);
1030      }
1031      const config = {
1032        headers: {
1033          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
1034        }
1035      }
1036      axios
1037        .post('/c/create/', params, config)
1038        .then(response => {
1039          this.message = '';
1040          this.$emit('set-active-entity', this.entity.urlApiDetailView)
1041          this.$emit('set-active-reply-form-comment-id', null)
1042
1043          //- Clear spacer around textarea, and comment preview.
1044          let textareaWrap = document.querySelector('.js-textarea-wrap');
1045          textareaWrap.dataset.replicatedValue = '';
1046          let commentPreview = document.querySelector('.js-comment-preview');
1047          commentPreview.replaceChildren();
1048
1049          this.isLoading = false;
1050        })
1051        .catch(error => {
1052          if (error.response) {
1053            if (error.response['data']) {
1054              Snackbar.show({text: error.response['data']['error'], icon: 'i-alert-triangle', showAction: false, customClass: 'error'});
1055            }
1056            this.isLoading = false;
1057          }
1058        });
1059    },
1060    setMarkdownStyle(textArea) {
1061      let markdown = '';
1062      switch(textArea.key) {
1063        case 'b':
1064          markdown = '**';
1065          break;
1066        case 'i':
1067          markdown = '*';
1068          break;
1069      }
1070
1071      let newMessage = this.message;
1072      let selectionStart = textArea.srcElement.selectionStart;
1073      let selectionEnd = textArea.srcElement.selectionEnd;
1074      let textToStyle = newMessage.substring(selectionStart, selectionEnd).trim();
1075
1076      newMessage = newMessage.substring(0, selectionStart) + markdown + textToStyle + markdown + newMessage.substring(selectionEnd);
1077      this.message = newMessage;
1078    },
1079  },
1080})
1081</script>
1081
1082
1083<script type="text/x-template" id="comment-list-item-template">
1084  <div>
1085    <div :id="'comment-' + comment.id" :class="{
1086        isAuthor: comment.isContentAuthor,
1087        isEditing: isEditing,
1088        isOwn: comment.isOwn,
1089        'has-replies': comment.replies && comment.replies.length != 0}" class="comment-item">
1090      <div class="comment-avatar">
1091        <user-avatar :size="'small'" :user="comment.user"></user-avatar>
1092      </div>
1093      <div class="comment-body">
1094        <div class="comment-header"><a :href="comment.user.url" :title="comment.user.username" class="comment-author"><span :title="comment.user.name" class="comment-username">{{ comment.user.username }}</span><i v-if="comment.isContentAuthor" title="Post Author" class="i-mic"></i><span v-if="comment.isAuthorAdmin" title="Admin" class="is-admin"><i class="i-shield"></i></span><span v-if="comment.isAuthorModerator" title="Moderator" class="is-moderator"><i class="i-shield"></i></span></a>
1095          <div v-if="comment.user.badges && comment.user.badges.length > 0" class="comment-badges badges-row">
1096            <div v-for="badge in comment.user.badges" :title="badge.name" class="badge is-user"><img :src="badge.urlImage" :alt="badge.name"/>
1097            </div>
1098          </div><a :href="entity.absoluteUrl + '#comment-' + comment.id" :title="comment.dateCreated" class="comment-date">{{ comment.naturalCreationTime }}
1099<i v-if="comment.isEdited === true" :title="'Edited on ' + comment.dateUpdated" class="i-edit"></i></a><span v-if="formErrorMessage">{{ formErrorMessage }}</span>
1100        </div>
1101        <div v-if="!isEditing" v-html="comment.content" class="comment-content"></div>
1102        <div v-else="v-else" class="comment-content">
1103          <div class="comment-form"><textarea @keydown.ctrl.enter="editComment" @keydown.meta.enter="editComment" v-model="message" placeholder="Edit the comment..." rows="8" class="form-control"></textarea>
1104            <div class="btn-toolbar-row">
1105              <button @click="toggleEditing" title="Cancel" type="button" class="btn btn-link"><span><i class="i-cancel"></i></span>
1106              </button>
1107              <button @click="editComment" title="Edit Comment" class="btn btn-comment"><span>Save Changes</span>
1108              </button>
1109            </div>
1110          </div>
1111        </div>
1112        <div class="comment-actions">
1113          <div class="btn-toolbar-row">
1114            <reactions v-if="!isEditing" v-on="$listeners" :comment-id="comment.id" :current-user="currentUser" :is-liked="comment.isLiked" :is-own="comment.isOwn" :likes-count="comment.likesCount" :like-toggle-url="comment.likeToggleUrl" :entity-url-api-detail-view="entity.urlApiDetailView" :url-user-list-liked="comment.urlUserListLiked"></reactions><a v-if="!isEditing" @click.stop="$emit('set-active-reply-form-comment-id', comment.id)" data-toggle="tooltip" href="#" title="Reply to this comment" class="btn-reply"><span><i class="i-reply"></i></span></a>
1115            <div v-if="!isEditing" class="dropdown"><a @click="toggleDropdown" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-link"><span><i class="i-more-vertical"></i></span></a>
1116              <div :class="{ show: isDropdownOpen }" class="dropdown-menu dropdown-menu-right"><a href="#" @click="toggleModalGenericForm(comment.urlReport, 'Report')" v-if="!comment.isOwn" title="Report Comment" class="dropdown-item"><span><i class="i-flag"></i>Report Comment...</span></a><a v-if="comment.isOwn || currentUser.isModerator" @click.stop="toggleEditing" href="#" class="dropdown-item"><span><i class="i-edit"></i>Edit</span></a>
1117                <div v-if="comment.isOwn || currentUser.isModerator" class="dropdown-divider"></div><a v-if="comment.isOwn || currentUser.isModerator" @click.stop="deleteComment" data-toggle="tooltip" href="#" title="Delete this comment" class="dropdown-item danger"><span><i class="i-trash"></i>Delete</span></a><a v-if="currentUser.isModerator" @click.stop="toggleModalGenericForm(comment.user.urlRemoveSpam, 'Remove Spam')" data-toggle="tooltip" href="#" title="Remove Spam" class="dropdown-item danger"><span><i class="i-trash"></i>
1117Remove Spam...</span></a>
1118                <div v-if="currentUser.isStaff" class="dropdown-divider"></div><a v-if="currentUser.isStaff" :href="comment.editAdminUrl" data-toggle="tooltip" title="Edit in Admin" class="dropdown-item is-admin"><span><i class="i-coffee"></i>Admin</span></a>
1119              </div>
1120            </div>
1121          </div>
1122        </div>
1123        <comment-form v-if="activeReplyFormCommentId === comment.id" v-on="$listeners" :current-user="currentUser" :parent-comment-id="comment.parentCommentId ? comment.parentCommentId : comment.id" :entity="entity"></comment-form>
1124      </div>
1125    </div>
1126    <comment-list v-on="$listeners" :active-reply-form-comment-id="activeReplyFormCommentId" :comments="comment.replies" :current-user="currentUser" :entity="entity" class="has-replies"></comment-list>
1127  </div>
1128</script>
vendor: 1 bytes, line 1128
1128
1129<script type="application/javascript">Vue.component('comment-list-item', {
1130  mixins: [dropdownMixin],
1131  template: '#comment-list-item-template',
1132  props: {
1133    entity: Object,
1134    comment: Object,
1135    currentUser: Object,
1136    parentCommentId: Boolean,
1137    activeReplyFormCommentId: Number,
1138    },
1139  data: function () {
1140    return {
1141      isEditing: false,
1142      message: this.comment.content,
1143      // Contains error message when failing to submit update
1144      formErrorMessage: null,
1145    }
1146  },
1147  methods: {
1148    deleteComment: function(data) {
1149      // Truncate comment content after 20 characters.
1150      commentContent = this.comment.contentRaw.slice(0, 20) + '...';
1151
1152      // Prompt confirm dialog box.
1153      if(confirm('Delete comment "' + commentContent + '"?'))
1154        axios
1155          .post(this.comment.deleteUrl)
1156          .then(response => {
1157            // TODO: Reduce comments.length or reload the comments list.
1158            commentDiv = document.getElementById('comment-' + this.comment.id)
1159            commentDiv.parentElement.parentElement.remove();
1160
1161            this.$emit('set-active-entity', this.entityUrlApiDetailView)
1162            })
1163    },
1164    toggleEditing: function () {
1165      this.formErrorMessage = null;
1166      if (this.isEditing) {
1167        this.isEditing = false;
1168      } else {
1169        this.message = this.comment.contentRaw;
1170        this.isEditing = true;
1171      }
1172    },
1173    editComment: function () {
1174      if (!this.message){
1175        Snackbar.show({text: 'Try writing something.', icon: 'i-alert-triangle', showAction: false, customClass: 'error'});
1176        this.isEditing = true;
1177        return;
1178      }
1179
1180      const params = new URLSearchParams();
1181      params.append('content', this.message);
1182      const config = {
1183        headers: {
1184          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
1185        }
1186      }
1187      axios
1188        .post(this.comment.editUrl, params, config)
1189        .then(response => {
1190          this.toggleEditing()
1191          this.$emit('set-active-entity', this.entityUrlApiDetailView)
1192          this.$emit('set-active-reply-form-comment-id', null)
1193          this.comment.content = response.data.content;
1194          this.comment.contentRaw = response.data.contentRaw;
1195
1196          Snackbar.show({text: 'Comment saved successfully.', icon: 'i-check', showAction: false, customClass: 'success'});
1197        })
1198        .catch(err => {
1199          if (err.response) {
1200            // client received an error response (5xx, 4xx)
1201            this.formErrorMessage = err.response.data.error
1202
1203            Snackbar.show({text: err.response.data.error, icon: 'i-alert-triangle', showAction: false, customClass: 'error'});
1204          }
1205        })
1206    },
1207    toggleModalGenericForm: function (urlForm, title) {
1208      this.$emit('toggle-modal', {
1209          type: 'generic_form',
1210          title: title,
1211          content: {'urlForm': urlForm}
1212        }
1213      )
1214    }
1215  }
1216})
1217</script>
1217
1218
1219<script type="text/x-template" id="comment-list-template">
1220  <ul v-if="comments && comments.length" class="comments-container">
1221    <li v-for="(comment, index) in comments" class="comment-item-container">
1222      <comment-list-item v-on="$listeners" :comment="comment" :entity="entity" :current-user="currentUser" :active-reply-form-comment-id="activeReplyFormCommentId"></comment-list-item>
1223    </li>
1224    <li v-if="urlApiCommentListViewNextPage">
1225      <button @click="$emit('append-entity-comments-from-next-page')" class="btn-secondary comments-more"><span>Load More</span>
1226      </button>
1227    </li>
1228  </ul>
1229</script>
vendor: 1 bytes, line 1229
1229
1230<script type="application/javascript">Vue.component('comment-list', {
1231  template: '#comment-list-template',
1232  props: {
1233    urlApiCommentListViewNextPage: String,
1234    activeReplyFormCommentId: Number,
1235    entity: Object,
1236    comments: Array,
1237    currentUser: Object
1238    }
1239})
1240</script>
1240
1241
1242
1243<script type="text/x-template" id="vote-template">
1244  <div class="btn-group">
1245    <div v-if="!entity.isOwn" @click="voteToggle" :class="{'is-voted': isVoted, 'is-loading': isLoading}" title="Vote" class="btn btn-theme btn-vote-arrow"><span><i :class="[direction === 'up' ? 'i-arrow-up' : 'i-arrow-down']"></i><span v-if="direction === 'up'">Upvote</span><span v-else="v-else">Downvote</span></span>
1246    </div>
1247    <div @click="toggleModalUserListVoted(direction)" title="Who voted for this?" class="btn btn-theme"><span><span v-if="direction === 'up'">{{ entity.votes.positiveCount }}</span><span v-else="v-else">{{ entity.votes.negativeCount }}</span><span v-if="entity.isOwn"><span v-if="direction === 'up'">&nbsp; Upvotes</span><span v-else="v-else">&nbsp; Downvotes</span></span></span>
1248    </div>
1249  </div>
1250</script>
vendor: 1 bytes, line 1250
1250
1251<script type="text/javascript">Vue.component('vote', {
1252  template: '#vote-template',
1253  props: {
1254    // Affect display of the component (either a simple icon, or a button)
1255    direction: {
1256      type: String,
1257      default: 'up',
1258      validator: function (value) {
1259        // The value must match one of these strings
1260        return ['up', 'down'].indexOf(value) !== -1
1261      }
1262    },
1263    currentUser: {
1264      type: Object,
1265      required: true,
1266      validator: function (user) {
1267        const expectedProps = ['isAuthenticated', 'username', 'name', 'avatar', 'url'];
1268        return expectedProps.every(item => user.hasOwnProperty(item));
1269      }
1270    },
1271    entity: Object,
1272  },
1273  data: function () {
1274    return {
1275      isLoading: false,
1276    }
1277  },
1278  methods: {
1279    voteToggle: function() {
1280      this.isLoading = true;
1281      const params = new URLSearchParams();
1282      params.set('direction', this.direction);
1283      const config = {
1284        headers: {
1285          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
1286        }
1287      }
1288      axios
1289        .post(this.entity.votes.voteToggleUrl, params, config)
1290        .then(response => {
1291          // TODO(fsiddi): Reduce the scope of this event.
1292          // We don't need to refresh the whole UI, rather only the likes counter
1293          this.$emit('set-active-entity', this.entity.urlApiDetailView)
1294          this.isLoading = false;
1295        })
1296    },
1297    // RCS Idea Entity method
1298    toggleModalUserListVoted: function (direction) {
1299      let title = direction === 'up' ? 'Who up voted this' : 'Who down voted this'
1300      let url = direction === 'up' ? this.entity.urlUserListVotedUp : this.entity.urlUserListVotedDown
1301      this.$emit('toggle-modal', {
1302          type: 'userList',
1303          title: title,
1304          content: {'urlUserList': url}
1305        }
1306      )
1307    },
1308  },
1309  computed: {
1310    votesCount: function () {
1311      return this.direction === 'up' ? this.entity.votes.positiveCount : this.entity.votes.negativeCount
1312    },
1313    isVoted: function () {
1314      return this.direction === 'up' ? this.entity.votes.isVotedPositive : this.entity.votes.isVotedNegative
1315    }
1316  },
1317})
1318</script>
1318
1319
1320<script type="text/x-template" id="filter-sort-toolbar-template">
1321  <div class="app-feed-toolbar">
1322    <div v-if="communityCategories.length" class="form-group form-group-h">
1323      <div>
1324        <select @change="queryChangeCategory($event)" v-model="queryCategory" title="Category" class="form-control">
1325          <option v-for="category in communityCategories" :title="category.name" :value="category.slug">{{ category.name }}</option>
1326        </select>
1327      </div>
1328      <div class="toolbar-right">
1329        <select @change="queryChangeDevelopmentStatus($event)" v-model="queryDevelopmentStatus" title="Filter by" class="form-control form-select-filter-by">
1330          <option v-for="status in developmentStatuses" :title="status.name" v-bind:value="status.slug">{{ status.name }}</option>
1331        </select>
1332        <select @change="queryChangeSorting($event)" v-model="querySorting" title="Sort by" class="form-control form-select-sort-by">
1333          <option v-for="order in sortingOrders" :title="order.name" v-bind:value="order.slug">{{ order.name }}</option>
1334        </select>
1335      </div>
1336    </div>
1337  </div>
1338</script>
vendor: 1 bytes, line 1338
1338
1339<script type="application/javascript">Vue.component('filter-sort-toolbar', {
1340template: '#filter-sort-toolbar-template',
1341props: {
1342  communityCategories: Array,
1343  developmentStatuses: Array,
1344  sortingOrders: Array,
1345  query: Object,
1346  queryExtension: Object
1347},
1348data: function () {
1349  return {
1350    queryDevelopmentStatus: "",
1351    queryCategory: "",
1352    querySorting: null,
1353  }
1354},
1355watch: {
1356  query: {
1357    deep: true,
1358    handler: function (val, oldVal) {
1359      this.queryCategory = val.category
1360      this.querySorting = val.sorting
1361    }
1362  },
1363  queryExtension: {
1364    deep: true,
1365    handler: function (val, oldVal) {
1366      this.queryDevelopmentStatus = val.developmentStatus
1367    }
1368  }
1369},
1370methods: {
1371  queryChangeCategory: function(event) {
1372    this.$emit('set-query-category', event.target.value)
1373  },
1374  queryChangeSorting: function (event) {
1375    this.$emit('set-query-sorting', event.target.value)
1376  },
1377  queryChangeDevelopmentStatus: function (event) {
1378    this.$emit('set-query-development-status', event.target.value)
1379  }
1380}
1381})
1382</script>
1382
1383
1384
1385<script type="text/javascript" src="https://blender.community/static/rcs/js/idea-list.b56b77a74ae6.js" charset="utf-8"></script>
1385
1386
1387
1388<script type="text/javascript" src="https://blender.community/static/js/tutti.f09baef4086e.js" charset="utf-8"></script>
1388
1389
1390
1391    
1391<script>tippy('[data-tooltip=true]');
1392
1393    </script>
vendor: 72 bytes, lines 1393-1395
1393
1394
1395    <script async="" src="https://www.googletagmanager.com/gtag/js?id=
1395UA-37082864-4
vendor: 16 bytes, lines 1395-1396
1395"></script>
1396    
1396<script>
vendor: 132 bytes, lines 1396-1401
1396window.dataLayer = window.dataLayer || [];
1397function gtag() {
1398  dataLayer.push(arguments);
1399}
1400gtag('js', new Date());
1401gtag('config', '
1401UA-37082864-4
vendor: 8 bytes, lines 1401-1402
1401');
1402    
1402</script>
1402
1403
1404  </body>
1405</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.